├── .editorconfig ├── .gitattributes ├── .github ├── actions │ └── setup │ │ └── action.yml └── workflows │ ├── ci.yml │ └── nextjs.yml ├── .gitignore ├── .nvmrc ├── .watchmanconfig ├── .yarn └── releases │ └── yarn-4.4.1.cjs ├── .yarnrc.yml ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── android ├── CMakeLists.txt ├── build.gradle ├── cpp-adapter.cpp ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── src │ ├── main │ ├── AndroidManifest.xml │ ├── AndroidManifestNew.xml │ └── java │ │ └── com │ │ └── fastopencv │ │ ├── FastOpencvModule.kt │ │ └── FastOpencvPackage.kt │ ├── newarch │ └── FastOpencvSpec.kt │ └── oldarch │ └── FastOpencvSpec.kt ├── babel.config.js ├── cpp ├── ConvertImage.cpp ├── ConvertImage.hpp ├── FOCV_Function.cpp ├── FOCV_Function.hpp ├── FOCV_FunctionArguments.cpp ├── FOCV_FunctionArguments.hpp ├── FOCV_Ids.cpp ├── FOCV_Ids.hpp ├── FOCV_JsiObject.cpp ├── FOCV_JsiObject.hpp ├── FOCV_Object.cpp ├── FOCV_Object.hpp ├── FOCV_Storage.cpp ├── FOCV_Storage.hpp ├── UUID.cpp ├── UUID.hpp ├── jsi │ ├── Promise.cpp │ ├── Promise.h │ ├── TypedArray.cpp │ └── TypedArray.h ├── react-native-fast-opencv.cpp └── react-native-fast-opencv.h ├── docs ├── README.md ├── images │ ├── blur-example.png │ ├── blur-phones.png │ ├── realtime-example.gif │ ├── realtime-phones.png │ └── title-banner.svg ├── next.config.js ├── package.json ├── pages │ ├── _meta.json │ ├── apidetails.md │ ├── availablefunctions.md │ ├── customized.md │ ├── examples │ │ ├── _meta.json │ │ ├── blur.md │ │ └── realtimedetection.md │ ├── index.md │ ├── installation.md │ ├── installation │ │ ├── _meta.json │ │ ├── visioncamera.md │ │ └── worklets.md │ └── usage.md ├── public │ ├── favicon.png │ └── favicon.svg ├── theme.config.jsx └── yarn.lock ├── example ├── .bundle │ └── config ├── .watchmanconfig ├── Gemfile ├── Gemfile.lock ├── README.md ├── android │ ├── app │ │ ├── build.gradle │ │ ├── debug.keystore │ │ ├── proguard-rules.pro │ │ └── src │ │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ │ └── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── java │ │ │ └── fastopencv │ │ │ │ └── example │ │ │ │ ├── MainActivity.kt │ │ │ │ └── MainApplication.kt │ │ │ └── res │ │ │ ├── drawable │ │ │ └── rn_edit_text_material.xml │ │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ └── values │ │ │ ├── strings.xml │ │ │ └── styles.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ └── settings.gradle ├── app.json ├── babel.config.js ├── index.js ├── ios │ ├── .xcode.env │ ├── .xcode.env.local │ ├── AppDelegate.swift │ ├── FastOpencvExample-Bridging-Header.h │ ├── FastOpencvExample.xcodeproj │ │ ├── project.pbxproj │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── FastOpencvExample.xcscheme │ ├── FastOpencvExample.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ ├── FastOpencvExample │ │ ├── Images.xcassets │ │ │ ├── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ │ └── Contents.json │ │ ├── Info.plist │ │ ├── LaunchScreen.storyboard │ │ └── PrivacyInfo.xcprivacy │ ├── FastOpencvExampleTests │ │ ├── FastOpencvExampleTests.m │ │ └── Info.plist │ ├── File.swift │ ├── Podfile │ └── Podfile.lock ├── jest.config.js ├── metro.config.js ├── package.json ├── react-native.config.js └── src │ ├── App.tsx │ ├── examples │ ├── CameraPassthrough.tsx │ ├── CameraRealtimeDetection.tsx │ ├── DocumentDetection.tsx │ └── ImageExample.tsx │ ├── home │ ├── Home.tsx │ └── Item.tsx │ └── types.ts ├── ios ├── FastOpencv.h └── FastOpencv.mm ├── lefthook.yml ├── package.json ├── react-native-fast-opencv.podspec ├── src ├── NativeFastOpencv.ts ├── constants │ ├── ColorConversionsCodes.ts │ ├── Core.ts │ ├── DataTypes.ts │ ├── ImageProcessing.ts │ └── ImageTransform.ts ├── functions │ ├── ColorConversion.ts │ ├── Core.ts │ └── ImageProcessing │ │ ├── ColorMap.ts │ │ ├── Drawing.ts │ │ ├── Feature.ts │ │ ├── ImageFiltering.ts │ │ ├── ImageTransform.ts │ │ ├── Misc.ts │ │ ├── ObjectDetection.ts │ │ └── Shape.ts ├── index.tsx ├── objects │ ├── ObjectType.ts │ └── Objects.ts └── utils │ └── UtilsFunctions.ts ├── tsconfig.build.json ├── tsconfig.json ├── turbo.json └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | [*] 8 | 9 | indent_style = space 10 | indent_size = 2 11 | 12 | end_of_line = lf 13 | charset = utf-8 14 | trim_trailing_whitespace = true 15 | insert_final_newline = true 16 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.pbxproj -text 2 | # specific for windows script files 3 | *.bat text eol=crlf -------------------------------------------------------------------------------- /.github/actions/setup/action.yml: -------------------------------------------------------------------------------- 1 | name: Setup 2 | description: Setup Node.js and install dependencies 3 | 4 | runs: 5 | using: composite 6 | steps: 7 | - name: Setup Node.js 8 | uses: actions/setup-node@v3 9 | with: 10 | node-version-file: .nvmrc 11 | 12 | - name: Cache dependencies 13 | id: yarn-cache 14 | uses: actions/cache@v3 15 | with: 16 | path: | 17 | **/node_modules 18 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 19 | restore-keys: | 20 | ${{ runner.os }}-yarn- 21 | 22 | - name: Install dependencies 23 | if: steps.yarn-cache.outputs.cache-hit != 'true' 24 | run: yarn install --immutable --frozen-lockfile 25 | shell: bash -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: 4 | branches: 5 | - main 6 | pull_request: 7 | branches: 8 | - main 9 | merge_group: 10 | types: 11 | - checks_requested 12 | 13 | jobs: 14 | lint: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - name: Checkout 18 | uses: actions/checkout@v3 19 | 20 | - name: Setup 21 | uses: ./.github/actions/setup 22 | 23 | - name: Lint files 24 | run: yarn lint 25 | 26 | - name: Typecheck files 27 | run: yarn typecheck 28 | 29 | test: 30 | runs-on: ubuntu-latest 31 | steps: 32 | - name: Checkout 33 | uses: actions/checkout@v3 34 | 35 | - name: Setup 36 | uses: ./.github/actions/setup 37 | 38 | - name: Run unit tests 39 | run: yarn test --maxWorkers=2 --coverage --passWithNoTests 40 | 41 | build-library: 42 | runs-on: ubuntu-latest 43 | steps: 44 | - name: Checkout 45 | uses: actions/checkout@v3 46 | 47 | - name: Setup 48 | uses: ./.github/actions/setup 49 | 50 | - name: Build package 51 | run: yarn prepare -------------------------------------------------------------------------------- /.github/workflows/nextjs.yml: -------------------------------------------------------------------------------- 1 | # Sample workflow for building and deploying a Next.js site to GitHub Pages 2 | # 3 | # To get started with Next.js see: https://nextjs.org/docs/getting-started 4 | # 5 | name: Deploy Next.js site to Pages 6 | 7 | defaults: 8 | run: 9 | working-directory: docs 10 | 11 | on: 12 | # Runs on pushes targeting the default branch 13 | push: 14 | branches: ["main"] 15 | 16 | # Allows you to run this workflow manually from the Actions tab 17 | workflow_dispatch: 18 | 19 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 20 | permissions: 21 | contents: read 22 | pages: write 23 | id-token: write 24 | 25 | # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. 26 | # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. 27 | concurrency: 28 | group: "pages" 29 | cancel-in-progress: false 30 | 31 | jobs: 32 | # Build job 33 | build: 34 | runs-on: ubuntu-latest 35 | steps: 36 | - name: Checkout 37 | uses: actions/checkout@v4 38 | - name: Detect package manager 39 | id: detect-package-manager 40 | run: | 41 | if [ -f "${{ github.workspace }}/yarn.lock" ]; then 42 | echo "manager=yarn" >> $GITHUB_OUTPUT 43 | echo "command=install" >> $GITHUB_OUTPUT 44 | echo "runner=yarn" >> $GITHUB_OUTPUT 45 | exit 0 46 | elif [ -f "${{ github.workspace }}/package.json" ]; then 47 | echo "manager=npm" >> $GITHUB_OUTPUT 48 | echo "command=ci" >> $GITHUB_OUTPUT 49 | echo "runner=npx --no-install" >> $GITHUB_OUTPUT 50 | exit 0 51 | else 52 | echo "Unable to determine package manager" 53 | exit 1 54 | fi 55 | - name: Setup Node 56 | uses: actions/setup-node@v4 57 | with: 58 | node-version: "20" 59 | - name: Setup Pages 60 | uses: actions/configure-pages@v5 61 | with: 62 | # Automatically inject basePath in your Next.js configuration file and disable 63 | # server side image optimization (https://nextjs.org/docs/api-reference/next/image#unoptimized). 64 | # 65 | # You may remove this line if you want to manage the configuration yourself. 66 | static_site_generator: next 67 | - name: Restore cache 68 | uses: actions/cache@v4 69 | with: 70 | path: | 71 | .next/cache 72 | # Generate a new cache whenever packages or source files change. 73 | key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json', '**/yarn.lock') }}-${{ hashFiles('**.[jt]s', '**.[jt]sx') }} 74 | # If source files changed but packages didn't, rebuild from a prior cache. 75 | restore-keys: | 76 | ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json', '**/yarn.lock') }}- 77 | - name: Install dependencies 78 | run: ${{ steps.detect-package-manager.outputs.manager }} ${{ steps.detect-package-manager.outputs.command }} 79 | - name: Build with Next.js 80 | run: ${{ steps.detect-package-manager.outputs.runner }} next build 81 | - name: Upload artifact 82 | uses: actions/upload-pages-artifact@v3 83 | with: 84 | path: ./docs/out 85 | 86 | # Deployment job 87 | deploy: 88 | environment: 89 | name: github-pages 90 | url: ${{ steps.deployment.outputs.page_url }} 91 | runs-on: ubuntu-latest 92 | needs: build 93 | steps: 94 | - name: Deploy to GitHub Pages 95 | id: deployment 96 | uses: actions/deploy-pages@v4 97 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # XDE 6 | .expo/ 7 | 8 | # VSCode 9 | .vscode/ 10 | jsconfig.json 11 | 12 | # Xcode 13 | # 14 | build/ 15 | *.pbxuser 16 | !default.pbxuser 17 | *.mode1v3 18 | !default.mode1v3 19 | *.mode2v3 20 | !default.mode2v3 21 | *.perspectivev3 22 | !default.perspectivev3 23 | xcuserdata 24 | *.xccheckout 25 | *.moved-aside 26 | DerivedData 27 | *.hmap 28 | *.ipa 29 | *.xcuserstate 30 | project.xcworkspace 31 | 32 | # Android/IJ 33 | # 34 | .classpath 35 | .cxx 36 | .gradle 37 | .idea 38 | .project 39 | .settings 40 | local.properties 41 | android.iml 42 | 43 | # Cocoapods 44 | # 45 | example/ios/Pods 46 | 47 | # Ruby 48 | example/vendor/ 49 | 50 | # node.js 51 | # 52 | node_modules/ 53 | npm-debug.log 54 | yarn-debug.log 55 | yarn-error.log 56 | 57 | # BUCK 58 | buck-out/ 59 | \.buckd/ 60 | android/app/libs 61 | android/keystores/debug.keystore 62 | 63 | # Yarn 64 | .yarn/* 65 | !.yarn/patches 66 | !.yarn/plugins 67 | !.yarn/releases 68 | !.yarn/sdks 69 | !.yarn/versions 70 | 71 | # Expo 72 | .expo/ 73 | 74 | # Turborepo 75 | .turbo/ 76 | 77 | # generated by bob 78 | lib/ 79 | 80 | .yarn/cache/* 81 | docs/.yarn 82 | .next 83 | docs/out/* 84 | .kotlin/ -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | v18 2 | -------------------------------------------------------------------------------- /.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | checksumBehavior: update 2 | 3 | compressionLevel: mixed 4 | 5 | enableGlobalCache: false 6 | 7 | nmHoistingLimits: workspaces 8 | 9 | nodeLinker: node-modules 10 | 11 | yarnPath: .yarn/releases/yarn-4.4.1.cjs 12 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | 2 | # Contributor Covenant Code of Conduct 3 | 4 | ## Our Pledge 5 | 6 | We as members, contributors, and leaders pledge to make participation in our 7 | community a harassment-free experience for everyone, regardless of age, body 8 | size, visible or invisible disability, ethnicity, sex characteristics, gender 9 | identity and expression, level of experience, education, socio-economic status, 10 | nationality, personal appearance, race, caste, color, religion, or sexual 11 | identity and orientation. 12 | 13 | We pledge to act and interact in ways that contribute to an open, welcoming, 14 | diverse, inclusive, and healthy community. 15 | 16 | ## Our Standards 17 | 18 | Examples of behavior that contributes to a positive environment for our 19 | community include: 20 | 21 | * Demonstrating empathy and kindness toward other people 22 | * Being respectful of differing opinions, viewpoints, and experiences 23 | * Giving and gracefully accepting constructive feedback 24 | * Accepting responsibility and apologizing to those affected by our mistakes, 25 | and learning from the experience 26 | * Focusing on what is best not just for us as individuals, but for the overall 27 | community 28 | 29 | Examples of unacceptable behavior include: 30 | 31 | * The use of sexualized language or imagery, and sexual attention or advances of 32 | any kind 33 | * Trolling, insulting or derogatory comments, and personal or political attacks 34 | * Public or private harassment 35 | * Publishing others' private information, such as a physical or email address, 36 | without their explicit permission 37 | * Other conduct which could reasonably be considered inappropriate in a 38 | professional setting 39 | 40 | ## Enforcement Responsibilities 41 | 42 | Community leaders are responsible for clarifying and enforcing our standards of 43 | acceptable behavior and will take appropriate and fair corrective action in 44 | response to any behavior that they deem inappropriate, threatening, offensive, 45 | or harmful. 46 | 47 | Community leaders have the right and responsibility to remove, edit, or reject 48 | comments, commits, code, wiki edits, issues, and other contributions that are 49 | not aligned to this Code of Conduct, and will communicate reasons for moderation 50 | decisions when appropriate. 51 | 52 | ## Scope 53 | 54 | This Code of Conduct applies within all community spaces, and also applies when 55 | an individual is officially representing the community in public spaces. 56 | Examples of representing our community include using an official e-mail address, 57 | posting via an official social media account, or acting as an appointed 58 | representative at an online or offline event. 59 | 60 | ## Enforcement 61 | 62 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 63 | reported to the community leaders responsible for enforcement at 64 | [INSERT CONTACT METHOD]. 65 | All complaints will be reviewed and investigated promptly and fairly. 66 | 67 | All community leaders are obligated to respect the privacy and security of the 68 | reporter of any incident. 69 | 70 | ## Enforcement Guidelines 71 | 72 | Community leaders will follow these Community Impact Guidelines in determining 73 | the consequences for any action they deem in violation of this Code of Conduct: 74 | 75 | ### 1. Correction 76 | 77 | **Community Impact**: Use of inappropriate language or other behavior deemed 78 | unprofessional or unwelcome in the community. 79 | 80 | **Consequence**: A private, written warning from community leaders, providing 81 | clarity around the nature of the violation and an explanation of why the 82 | behavior was inappropriate. A public apology may be requested. 83 | 84 | ### 2. Warning 85 | 86 | **Community Impact**: A violation through a single incident or series of 87 | actions. 88 | 89 | **Consequence**: A warning with consequences for continued behavior. No 90 | interaction with the people involved, including unsolicited interaction with 91 | those enforcing the Code of Conduct, for a specified period of time. This 92 | includes avoiding interactions in community spaces as well as external channels 93 | like social media. Violating these terms may lead to a temporary or permanent 94 | ban. 95 | 96 | ### 3. Temporary Ban 97 | 98 | **Community Impact**: A serious violation of community standards, including 99 | sustained inappropriate behavior. 100 | 101 | **Consequence**: A temporary ban from any sort of interaction or public 102 | communication with the community for a specified period of time. No public or 103 | private interaction with the people involved, including unsolicited interaction 104 | with those enforcing the Code of Conduct, is allowed during this period. 105 | Violating these terms may lead to a permanent ban. 106 | 107 | ### 4. Permanent Ban 108 | 109 | **Community Impact**: Demonstrating a pattern of violation of community 110 | standards, including sustained inappropriate behavior, harassment of an 111 | individual, or aggression toward or disparagement of classes of individuals. 112 | 113 | **Consequence**: A permanent ban from any sort of public interaction within the 114 | community. 115 | 116 | ## Attribution 117 | 118 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 119 | version 2.1, available at 120 | [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. 121 | 122 | Community Impact Guidelines were inspired by 123 | [Mozilla's code of conduct enforcement ladder][Mozilla CoC]. 124 | 125 | For answers to common questions about this code of conduct, see the FAQ at 126 | [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at 127 | [https://www.contributor-covenant.org/translations][translations]. 128 | 129 | [homepage]: https://www.contributor-covenant.org 130 | [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html 131 | [Mozilla CoC]: https://github.com/mozilla/diversity 132 | [FAQ]: https://www.contributor-covenant.org/faq 133 | [translations]: https://www.contributor-covenant.org/translations 134 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Contributions are always welcome, no matter how large or small! 4 | 5 | We want this community to be friendly and respectful to each other. Please follow it in all your interactions with the project. Before contributing, please read the [code of conduct](./CODE_OF_CONDUCT.md). 6 | 7 | ## Development workflow 8 | 9 | This project is a monorepo managed using [Yarn workspaces](https://yarnpkg.com/features/workspaces). It contains the following packages: 10 | 11 | - The library package in the root directory. 12 | - An example app in the `example/` directory. 13 | 14 | To get started with the project, run `yarn` in the root directory to install the required dependencies for each package: 15 | 16 | ```sh 17 | yarn 18 | ``` 19 | 20 | > Since the project relies on Yarn workspaces, you cannot use [`npm`](https://github.com/npm/cli) for development. 21 | 22 | The [example app](/example/) demonstrates usage of the library. You need to run it to test any changes you make. 23 | 24 | It is configured to use the local version of the library, so any changes you make to the library's source code will be reflected in the example app. Changes to the library's JavaScript code will be reflected in the example app without a rebuild, but native code changes will require a rebuild of the example app. 25 | 26 | If you want to use Android Studio or XCode to edit the native code, you can open the `example/android` or `example/ios` directories respectively in those editors. To edit the Objective-C or Swift files, open `example/ios/FastOpencvExample.xcworkspace` in XCode and find the source files at `Pods > Development Pods > react-native-fast-opencv`. 27 | 28 | To edit the Java or Kotlin files, open `example/android` in Android studio and find the source files at `react-native-fast-opencv` under `Android`. 29 | 30 | You can use various commands from the root directory to work with the project. 31 | 32 | To start the packager: 33 | 34 | ```sh 35 | yarn example start 36 | ``` 37 | 38 | To run the example app on Android: 39 | 40 | ```sh 41 | yarn example android 42 | ``` 43 | 44 | To run the example app on iOS: 45 | 46 | ```sh 47 | yarn example ios 48 | ``` 49 | 50 | By default, the example is configured to build with the old architecture. To run the example with the new architecture, you can do the following: 51 | 52 | 1. For Android, run: 53 | 54 | ```sh 55 | ORG_GRADLE_PROJECT_newArchEnabled=true yarn example android 56 | ``` 57 | 58 | 2. For iOS, run: 59 | 60 | ```sh 61 | cd example/ios 62 | RCT_NEW_ARCH_ENABLED=1 pod install 63 | cd - 64 | yarn example ios 65 | ``` 66 | 67 | If you are building for a different architecture than your previous build, make sure to remove the build folders first. You can run the following command to cleanup all build folders: 68 | 69 | ```sh 70 | yarn clean 71 | ``` 72 | 73 | To confirm that the app is running with the new architecture, you can check the Metro logs for a message like this: 74 | 75 | ```sh 76 | Running "FastOpencvExample" with {"fabric":true,"initialProps":{"concurrentRoot":true},"rootTag":1} 77 | ``` 78 | 79 | Note the `"fabric":true` and `"concurrentRoot":true` properties. 80 | 81 | Make sure your code passes TypeScript and ESLint. Run the following to verify: 82 | 83 | ```sh 84 | yarn typecheck 85 | yarn lint 86 | ``` 87 | 88 | To fix formatting errors, run the following: 89 | 90 | ```sh 91 | yarn lint --fix 92 | ``` 93 | 94 | Remember to add tests for your change if possible. Run the unit tests by: 95 | 96 | ```sh 97 | yarn test 98 | ``` 99 | 100 | ### Commit message convention 101 | 102 | We follow the [conventional commits specification](https://www.conventionalcommits.org/en) for our commit messages: 103 | 104 | - `fix`: bug fixes, e.g. fix crash due to deprecated method. 105 | - `feat`: new features, e.g. add new method to the module. 106 | - `refactor`: code refactor, e.g. migrate from class components to hooks. 107 | - `docs`: changes into documentation, e.g. add usage example for the module.. 108 | - `test`: adding or updating tests, e.g. add integration tests using detox. 109 | - `chore`: tooling changes, e.g. change CI config. 110 | 111 | Our pre-commit hooks verify that your commit message matches this format when committing. 112 | 113 | ### Linting and tests 114 | 115 | [ESLint](https://eslint.org/), [Prettier](https://prettier.io/), [TypeScript](https://www.typescriptlang.org/) 116 | 117 | We use [TypeScript](https://www.typescriptlang.org/) for type checking, [ESLint](https://eslint.org/) with [Prettier](https://prettier.io/) for linting and formatting the code, and [Jest](https://jestjs.io/) for testing. 118 | 119 | Our pre-commit hooks verify that the linter and tests pass when committing. 120 | 121 | ### Publishing to npm 122 | 123 | We use [release-it](https://github.com/release-it/release-it) to make it easier to publish new versions. It handles common tasks like bumping version based on semver, creating tags and releases etc. 124 | 125 | To publish new versions, run the following: 126 | 127 | ```sh 128 | yarn release 129 | ``` 130 | 131 | ### Scripts 132 | 133 | The `package.json` file contains various scripts for common tasks: 134 | 135 | - `yarn`: setup project by installing dependencies. 136 | - `yarn typecheck`: type-check files with TypeScript. 137 | - `yarn lint`: lint files with ESLint. 138 | - `yarn test`: run unit tests with Jest. 139 | - `yarn example start`: start the Metro server for the example app. 140 | - `yarn example android`: run the example app on Android. 141 | - `yarn example ios`: run the example app on iOS. 142 | 143 | ### Sending a pull request 144 | 145 | > **Working on your first pull request?** You can learn how from this _free_ series: [How to Contribute to an Open Source Project on GitHub](https://app.egghead.io/playlists/how-to-contribute-to-an-open-source-project-on-github). 146 | 147 | When you're sending a pull request: 148 | 149 | - Prefer small pull requests focused on one change. 150 | - Verify that linters and tests are passing. 151 | - Review the documentation to make sure it looks good. 152 | - Follow the pull request template when opening a pull request. 153 | - For pull requests that change the API or implementation, discuss with maintainers first by opening an issue. 154 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Łukasz Kurant 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![React Native Fast OpenCV](./docs/images/title-banner.svg) 2 | 3 | A powerful port of **OpenCV** for React Native. 4 | 5 | > The library is in the early stages of development and not all functions or objects available in OpenCV are supported. Add an issue if you have any problems or questions. 6 | 7 | - 🔥 Powered by JSI 8 | - 🏎️ Uses OpenCV C++ API 9 | - 🏛️ New architecture ready 10 | - 🪽 Easy usage, without manual configuration 11 | - 📸 Easy [VisionCamera](https://github.com/mrousavy/react-native-vision-camera) integration 12 | - 🧵 Easy [WorkletsCore](https://github.com/margelo/react-native-worklets-core) integration 13 | - ⛓️‍💥 It can also be used on its own without integration 14 | 15 | ### Motivation 16 | 17 | OpenCV is a popular library for image processing, but quite tricky when it comes to its integration and use in React Native applications. My main goal is to enable simple operations directly from JavaScript code. 18 | 19 | Currently, there is a port of the OpenCV library but due to the change in standards when developing React Native applications, I decided to create a new library using an API in C++ that will allow for simpler integration, better performance and readability when used in code. 20 | 21 | ### Documentation 22 | 23 | Docs are available [here](https://lukaszkurantdev.github.io/react-native-fast-opencv). 24 | 25 | ### State of development 26 | 27 | Due to the size of the OpenCV library, this port currently only supports selected objects and functions. However, development is simple enough to only require the addition of specific code in C++ using the functionality. 28 | 29 | List of available functions are available [here](https://lukaszkurantdev.github.io/react-native-fast-opencv/availablefunctions). 30 | 31 | --- 32 | 33 | ### Credits 34 | Special thanks to: 35 | - The creators of the [Vision Camera](https://github.com/mrousavy/react-native-vision-camera), [WorkletsCore](https://github.com/margelo/react-native-worklets-core) and [FastTFLite](https://github.com/mrousavy/react-native-fast-tflite) libraries, especially Marc Rousavy - thanks for the amazing libraries. 36 | - Creator of the [react-native-opencv3](https://github.com/adamgf/react-native-opencv3) library (Adam G. Freeman) - your library was an important starting point for creating my port. 37 | - Creator of the [TS definition for OpenCV](https://github.com/peteruhnak/opencv-ts) (Peter Uhnak) - the library was an important support for this port. 38 | 39 | 40 | 41 | ### License 42 | MIT © 2024-2025 Lukasz Kurant 43 | 44 | -------------------------------------------------------------------------------- /android/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.4.1) 2 | project(FastOpencv) 3 | 4 | set (CMAKE_VERBOSE_MAKEFILE ON) 5 | set (CMAKE_CXX_STANDARD 20) 6 | 7 | file(TO_CMAKE_PATH "${NODE_MODULES_DIR}" NODE_MODULES_DIR) 8 | 9 | find_package(ReactAndroid REQUIRED CONFIG) 10 | find_package(fbjni REQUIRED CONFIG) 11 | find_package(OpenCV REQUIRED COMPONENTS OpenCV::opencv_java4) 12 | 13 | add_library(react-native-fast-opencv 14 | SHARED 15 | "${NODE_MODULES_DIR}/react-native/ReactCommon/jsi/jsi/jsi.cpp" 16 | ../cpp/react-native-fast-opencv.cpp 17 | ../cpp/react-native-fast-opencv.h 18 | cpp-adapter.cpp 19 | ../cpp/ConvertImage.cpp 20 | ../cpp/FOCV_Function.cpp 21 | ../cpp/FOCV_FunctionArguments.cpp 22 | ../cpp/FOCV_Ids.cpp 23 | ../cpp/FOCV_JsiObject.cpp 24 | ../cpp/FOCV_Object.cpp 25 | ../cpp/FOCV_Storage.cpp 26 | ../cpp/FOCV_Storage.hpp 27 | ../cpp/UUID.cpp 28 | ../cpp/jsi/TypedArray.cpp 29 | ../cpp/jsi/Promise.cpp 30 | 31 | ) 32 | 33 | # Specifies a path to native header files. 34 | include_directories( 35 | ../cpp 36 | ../cpp/jsi 37 | "${NODE_MODULES_DIR}/react-native/React" 38 | "${NODE_MODULES_DIR}/react-native/React/Base" 39 | "${NODE_MODULES_DIR}/react-native/ReactCommon" 40 | "${NODE_MODULES_DIR}/react-native/ReactCommon/jsi" 41 | "${NODE_MODULES_DIR}/react-native/ReactCommon/callinvoker" 42 | "${NODE_MODULES_DIR}/react-native/ReactAndroid/src/main/jni/react/turbomodule" 43 | ) 44 | 45 | if(ReactAndroid_VERSION_MINOR GREATER_EQUAL 76) 46 | target_link_libraries( 47 | react-native-fast-opencv 48 | log 49 | android 50 | ReactAndroid::reactnative 51 | fbjni::fbjni 52 | OpenCV::opencv_java4 53 | ) 54 | else() 55 | target_link_libraries( 56 | react-native-fast-opencv 57 | log 58 | android 59 | ReactAndroid::folly_runtime 60 | ReactAndroid::glog 61 | ReactAndroid::jsi 62 | ReactAndroid::reactnativejni 63 | fbjni::fbjni 64 | OpenCV::opencv_java4 65 | ) 66 | endif() -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | import java.nio.file.Paths 2 | 3 | buildscript { 4 | // Buildscript is evaluated before everything else so we can't use getExtOrDefault 5 | def kotlin_version = rootProject.ext.has("kotlinVersion") ? rootProject.ext.get("kotlinVersion") : project.properties["FastOpencv_kotlinVersion"] 6 | 7 | repositories { 8 | google() 9 | mavenCentral() 10 | } 11 | 12 | dependencies { 13 | classpath "com.android.tools.build:gradle:8.6.0" 14 | // noinspection DifferentKotlinGradleVersion 15 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 16 | } 17 | } 18 | 19 | def reactNativeArchitectures() { 20 | def value = rootProject.getProperties().get("reactNativeArchitectures") 21 | return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"] 22 | } 23 | 24 | def isNewArchitectureEnabled() { 25 | return rootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true" 26 | } 27 | 28 | apply plugin: "com.android.library" 29 | apply plugin: "kotlin-android" 30 | 31 | if (isNewArchitectureEnabled()) { 32 | apply plugin: "com.facebook.react" 33 | } 34 | 35 | def getExtOrDefault(name) { 36 | return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties["FastOpencv_" + name] 37 | } 38 | 39 | def getExtOrIntegerDefault(name) { 40 | return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["FastOpencv_" + name]).toInteger() 41 | } 42 | 43 | def supportsNamespace() { 44 | def parsed = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION.tokenize('.') 45 | def major = parsed[0].toInteger() 46 | def minor = parsed[1].toInteger() 47 | 48 | // Namespace support was added in 7.3.0 49 | return (major == 7 && minor >= 3) || major >= 8 50 | } 51 | 52 | static def findNodeModules(baseDir) { 53 | def basePath = baseDir.toPath().normalize() 54 | // Node's module resolution algorithm searches up to the root directory, 55 | // after which the base path will be null 56 | while (basePath) { 57 | def nodeModulesPath = Paths.get(basePath.toString(), "node_modules") 58 | def reactNativePath = Paths.get(nodeModulesPath.toString(), "react-native") 59 | if (nodeModulesPath.toFile().exists() && reactNativePath.toFile().exists()) { 60 | return nodeModulesPath.toString() 61 | } 62 | basePath = basePath.getParent() 63 | } 64 | throw new GradleException("react-native-fast-opencv: Failed to find node_modules/ path!") 65 | } 66 | 67 | def nodeModules = findNodeModules(projectDir) 68 | 69 | android { 70 | if (supportsNamespace()) { 71 | namespace "com.fastopencv" 72 | 73 | sourceSets { 74 | main { 75 | manifest.srcFile "src/main/AndroidManifestNew.xml" 76 | } 77 | } 78 | } 79 | 80 | ndkVersion getExtOrDefault("ndkVersion") 81 | compileSdkVersion getExtOrIntegerDefault("compileSdkVersion") 82 | 83 | defaultConfig { 84 | minSdkVersion getExtOrIntegerDefault("minSdkVersion") 85 | targetSdkVersion getExtOrIntegerDefault("targetSdkVersion") 86 | buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString() 87 | 88 | buildFeatures { 89 | prefab = true 90 | } 91 | 92 | externalNativeBuild { 93 | cmake { 94 | arguments "-DANDROID_STL=c++_shared", 95 | "-DNODE_MODULES_DIR=${nodeModules}" 96 | cppFlags "-O2 -frtti -fexceptions -Wall -fstack-protector-all" 97 | abiFilters(*reactNativeArchitectures()) 98 | } 99 | } 100 | } 101 | packagingOptions { 102 | excludes = [ 103 | "META-INF", 104 | "META-INF/**", 105 | "**/libc++_shared.so", 106 | "**/libfbjni.so", 107 | "**/libjsi.so", 108 | "**/libreactnative.so", 109 | "**/libreactnativejni.so", 110 | "**/libturbomodulejsijni.so", 111 | "**/libreact_nativemodule_core.so", 112 | "**/libfolly_runtime.so", 113 | "**/libglog.so" 114 | ] 115 | } 116 | 117 | externalNativeBuild { 118 | cmake { 119 | path "CMakeLists.txt" 120 | } 121 | } 122 | 123 | buildFeatures { 124 | buildConfig true 125 | } 126 | 127 | buildTypes { 128 | release { 129 | minifyEnabled false 130 | } 131 | } 132 | 133 | lintOptions { 134 | disable "GradleCompatible" 135 | } 136 | 137 | compileOptions { 138 | sourceCompatibility JavaVersion.VERSION_1_8 139 | targetCompatibility JavaVersion.VERSION_1_8 140 | } 141 | 142 | sourceSets { 143 | main { 144 | if (isNewArchitectureEnabled()) { 145 | java.srcDirs += [ 146 | "src/newarch", 147 | // This is needed to build Kotlin project with NewArch enabled 148 | "${project.buildDir}/generated/source/codegen/java" 149 | ] 150 | } else { 151 | java.srcDirs += ["src/oldarch"] 152 | } 153 | } 154 | } 155 | } 156 | 157 | repositories { 158 | mavenCentral() 159 | google() 160 | } 161 | 162 | def kotlin_version = getExtOrDefault("kotlinVersion") 163 | 164 | dependencies { 165 | // For < 0.71, this will be from the local maven repo 166 | // For > 0.71, this will be replaced by `com.facebook.react:react-android:$version` by react gradle plugin 167 | //noinspection GradleDynamicVersion 168 | implementation "com.facebook.react:react-android:0.74.4" 169 | implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" 170 | implementation 'org.opencv:opencv:4.9.0' 171 | } 172 | 173 | if (isNewArchitectureEnabled()) { 174 | react { 175 | jsRootDir = file("../src/") 176 | libraryName = "FastOpencv" 177 | codegenJavaPackageName = "com.fastopencv" 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /android/cpp-adapter.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "react-native-fast-opencv.h" 5 | #include 6 | 7 | using namespace facebook; 8 | 9 | extern "C" 10 | JNIEXPORT void JNICALL 11 | Java_com_fastopencv_FastOpencvModule_nativeInstall(JNIEnv *env, jobject thiz, jlong jsi_runtime_ref, 12 | jobject js_call_invoker_holder) { 13 | auto jsiRuntime{ reinterpret_cast(jsi_runtime_ref) }; 14 | auto jsCallInvoker{ jni::alias_ref{reinterpret_cast(js_call_invoker_holder) }->cthis()->getCallInvoker() }; 15 | 16 | // RNWorklet::JsiWorkletContext::getDefaultInstance()->initialize( 17 | // "default", jsiRuntime, [=](std::function &&f) { 18 | // jsCallInvoker->invokeAsync(std::move(f)); 19 | // }); 20 | 21 | OpenCVPlugin::installOpenCV(*jsiRuntime, jsCallInvoker); 22 | } 23 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | FastOpencv_kotlinVersion=1.9.0 2 | FastOpencv_minSdkVersion=24 3 | FastOpencv_targetSdkVersion=34 4 | FastOpencv_compileSdkVersion=34 5 | FastOpencv_ndkVersion=21.4.7075529 6 | android.useAndroidX=true 7 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukaszkurantdev/react-native-fast-opencv/56c99571718056de4f466d9809913325c2e721e0/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | @rem SPDX-License-Identifier: Apache-2.0 17 | @rem 18 | 19 | @if "%DEBUG%"=="" @echo off 20 | @rem ########################################################################## 21 | @rem 22 | @rem Gradle startup script for Windows 23 | @rem 24 | @rem ########################################################################## 25 | 26 | @rem Set local scope for the variables with windows NT shell 27 | if "%OS%"=="Windows_NT" setlocal 28 | 29 | set DIRNAME=%~dp0 30 | if "%DIRNAME%"=="" set DIRNAME=. 31 | @rem This is normally unused 32 | set APP_BASE_NAME=%~n0 33 | set APP_HOME=%DIRNAME% 34 | 35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 37 | 38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 40 | 41 | @rem Find java.exe 42 | if defined JAVA_HOME goto findJavaFromJavaHome 43 | 44 | set JAVA_EXE=java.exe 45 | %JAVA_EXE% -version >NUL 2>&1 46 | if %ERRORLEVEL% equ 0 goto execute 47 | 48 | echo. 1>&2 49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 50 | echo. 1>&2 51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 52 | echo location of your Java installation. 1>&2 53 | 54 | goto fail 55 | 56 | :findJavaFromJavaHome 57 | set JAVA_HOME=%JAVA_HOME:"=% 58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 59 | 60 | if exist "%JAVA_EXE%" goto execute 61 | 62 | echo. 1>&2 63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 64 | echo. 1>&2 65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 66 | echo location of your Java installation. 1>&2 67 | 68 | goto fail 69 | 70 | :execute 71 | @rem Setup the command line 72 | 73 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 74 | 75 | 76 | @rem Execute Gradle 77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 78 | 79 | :end 80 | @rem End local scope for the variables with windows NT shell 81 | if %ERRORLEVEL% equ 0 goto mainEnd 82 | 83 | :fail 84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 85 | rem the _cmd.exe /c_ return code! 86 | set EXIT_CODE=%ERRORLEVEL% 87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 89 | exit /b %EXIT_CODE% 90 | 91 | :mainEnd 92 | if "%OS%"=="Windows_NT" endlocal 93 | 94 | :omega 95 | -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /android/src/main/AndroidManifestNew.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /android/src/main/java/com/fastopencv/FastOpencvModule.kt: -------------------------------------------------------------------------------- 1 | package com.fastopencv 2 | 3 | import android.util.Log 4 | import com.facebook.react.bridge.ReactApplicationContext 5 | import com.facebook.react.bridge.ReactMethod 6 | import com.facebook.react.turbomodule.core.interfaces.CallInvokerHolder 7 | 8 | 9 | class FastOpencvModule internal constructor(context: ReactApplicationContext) : 10 | FastOpencvSpec(context) { 11 | 12 | override fun getName(): String { 13 | return NAME 14 | } 15 | 16 | companion object { 17 | const val NAME = "FastOpencv" 18 | 19 | init { 20 | System.loadLibrary("react-native-fast-opencv") 21 | } 22 | } 23 | 24 | private external fun nativeInstall(jsiRuntimeRef: Long, jsCallInvokerHolder: CallInvokerHolder?) 25 | 26 | @ReactMethod(isBlockingSynchronousMethod = true) 27 | override fun install(): Boolean { 28 | val jsContext = reactApplicationContext.javaScriptContextHolder 29 | 30 | try { 31 | if (jsContext == null) { 32 | Log.e(NAME, "React Application Context was null!") 33 | return false 34 | } 35 | val jsiRuntimeRef = jsContext!!.get() 36 | val jsCallInvokerHolder = reactApplicationContext.catalystInstance.jsCallInvokerHolder 37 | 38 | nativeInstall(jsiRuntimeRef, jsCallInvokerHolder) 39 | 40 | return true 41 | } catch (exception: Exception) { 42 | Log.e(NAME, "Failed to initialize react-native-worklets-core!", exception) 43 | 44 | } 45 | 46 | return false 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /android/src/main/java/com/fastopencv/FastOpencvPackage.kt: -------------------------------------------------------------------------------- 1 | package com.fastopencv 2 | 3 | import com.facebook.react.TurboReactPackage 4 | import com.facebook.react.bridge.ReactApplicationContext 5 | import com.facebook.react.bridge.NativeModule 6 | import com.facebook.react.module.model.ReactModuleInfoProvider 7 | import com.facebook.react.module.model.ReactModuleInfo 8 | import java.util.HashMap 9 | 10 | class FastOpencvPackage : TurboReactPackage() { 11 | override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? { 12 | return if (name == FastOpencvModule.NAME) { 13 | FastOpencvModule(reactContext) 14 | } else { 15 | null 16 | } 17 | } 18 | 19 | override fun getReactModuleInfoProvider(): ReactModuleInfoProvider { 20 | return ReactModuleInfoProvider { 21 | val moduleInfos: MutableMap = HashMap() 22 | val isTurboModule: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED 23 | moduleInfos[FastOpencvModule.NAME] = ReactModuleInfo( 24 | FastOpencvModule.NAME, 25 | FastOpencvModule.NAME, 26 | false, // canOverrideExistingModule 27 | false, // needsEagerInit 28 | true, // hasConstants 29 | false, // isCxxModule 30 | isTurboModule // isTurboModule 31 | ) 32 | moduleInfos 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /android/src/newarch/FastOpencvSpec.kt: -------------------------------------------------------------------------------- 1 | package com.fastopencv 2 | 3 | import com.facebook.react.bridge.ReactApplicationContext 4 | 5 | abstract class FastOpencvSpec internal constructor(context: ReactApplicationContext) : 6 | NativeFastOpencvSpec(context) { 7 | } 8 | -------------------------------------------------------------------------------- /android/src/oldarch/FastOpencvSpec.kt: -------------------------------------------------------------------------------- 1 | package com.fastopencv 2 | 3 | import com.facebook.react.bridge.ReactApplicationContext 4 | import com.facebook.react.bridge.ReactContextBaseJavaModule 5 | import com.facebook.react.bridge.Promise 6 | 7 | abstract class FastOpencvSpec internal constructor(context: ReactApplicationContext) : 8 | ReactContextBaseJavaModule(context) { 9 | 10 | abstract fun install(): Boolean 11 | } 12 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | ['module:react-native-builder-bob/babel-preset', { modules: 'commonjs' }], 4 | ], 5 | }; 6 | -------------------------------------------------------------------------------- /cpp/ConvertImage.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // ConvertImage.cpp 3 | // react-native-fast-opencv 4 | // 5 | // Created by Łukasz Kurant on 13/08/2024. 6 | // 7 | // This methods are from https://stackoverflow.com/a/32270463 8 | // Credits to Miki 9 | 10 | #include "ConvertImage.hpp" 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | 18 | using namespace std; 19 | using namespace cv; 20 | 21 | static const std::string base64_chars = 22 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 23 | "abcdefghijklmnopqrstuvwxyz" 24 | "0123456789+/"; 25 | 26 | static inline bool is_base64( unsigned char c ) 27 | { 28 | return (isalnum(c) || (c == '+') || (c == '/')); 29 | } 30 | 31 | std::string ImageConverter::base64_encode(uchar const* bytes_to_encode, unsigned int in_len) 32 | { 33 | std::string ret; 34 | 35 | int i = 0; 36 | int j = 0; 37 | unsigned char char_array_3[3]; 38 | unsigned char char_array_4[4]; 39 | 40 | while (in_len--) 41 | { 42 | char_array_3[i++] = *(bytes_to_encode++); 43 | if (i == 3) 44 | { 45 | char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; 46 | char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); 47 | char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); 48 | char_array_4[3] = char_array_3[2] & 0x3f; 49 | 50 | for (i = 0; (i <4); i++) 51 | { 52 | ret += base64_chars[char_array_4[i]]; 53 | } 54 | i = 0; 55 | } 56 | } 57 | 58 | if (i) 59 | { 60 | for (j = i; j < 3; j++) 61 | { 62 | char_array_3[j] = '\0'; 63 | } 64 | 65 | char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; 66 | char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); 67 | char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); 68 | char_array_4[3] = char_array_3[2] & 0x3f; 69 | 70 | for (j = 0; (j < i + 1); j++) 71 | { 72 | ret += base64_chars[char_array_4[j]]; 73 | } 74 | 75 | while ((i++ < 3)) 76 | { 77 | ret += '='; 78 | } 79 | } 80 | 81 | return ret; 82 | } 83 | 84 | std::string ImageConverter::base64_decode(std::string const& encoded_string) 85 | { 86 | int in_len = encoded_string.size(); 87 | int i = 0; 88 | int j = 0; 89 | int in_ = 0; 90 | unsigned char char_array_4[4], char_array_3[3]; 91 | std::string ret; 92 | 93 | while (in_len-- && (encoded_string[in_] != '=') && is_base64(encoded_string[in_])) 94 | { 95 | char_array_4[i++] = encoded_string[in_]; in_++; 96 | 97 | if (i == 4) 98 | { 99 | for (i = 0; i < 4; i++) 100 | { 101 | char_array_4[i] = base64_chars.find(char_array_4[i]); 102 | } 103 | 104 | char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); 105 | char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); 106 | char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; 107 | 108 | for (i = 0; (i < 3); i++) 109 | { 110 | ret += char_array_3[i]; 111 | } 112 | 113 | i = 0; 114 | } 115 | } 116 | 117 | if (i) 118 | { 119 | for (j = i; j < 4; j++) 120 | { 121 | char_array_4[j] = 0; 122 | } 123 | 124 | for (j = 0; j < 4; j++) 125 | { 126 | char_array_4[j] = base64_chars.find(char_array_4[j]); 127 | } 128 | 129 | char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); 130 | char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); 131 | char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; 132 | 133 | for (j = 0; (j < i - 1); j++) 134 | { 135 | ret += char_array_3[j]; 136 | } 137 | } 138 | 139 | return ret; 140 | } 141 | 142 | string ImageConverter::mat2str(const Mat& m, std::string &format) 143 | { 144 | vector buf; 145 | cv::imencode("." + format, m, buf); 146 | auto result = reinterpret_cast(buf.data()); 147 | 148 | return base64_encode(result, buf.size()); 149 | 150 | } 151 | 152 | Mat ImageConverter::str2mat(const string& s) 153 | { 154 | // Decode data 155 | string decoded_string = base64_decode(s); 156 | vector data(decoded_string.begin(), decoded_string.end()); 157 | 158 | cv::Mat img = cv::imdecode(data, IMREAD_UNCHANGED); 159 | 160 | return img; 161 | } 162 | -------------------------------------------------------------------------------- /cpp/ConvertImage.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // ConvertImage.hpp 3 | // react-native-fast-opencv 4 | // 5 | // Created by Łukasz Kurant on 13/08/2024. 6 | // 7 | 8 | #ifndef ConvertImage_hpp 9 | #define ConvertImage_hpp 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | 19 | using namespace std; 20 | using namespace cv; 21 | 22 | class ImageConverter { 23 | public: 24 | static cv::Mat str2mat(const string& imageBase64); 25 | static string mat2str(const Mat& img, std::string &format); 26 | 27 | private: 28 | static std::string base64_encode(uchar const* bytesToEncode, unsigned int inLen); 29 | static std::string base64_decode(std::string const& encodedString); 30 | }; 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /cpp/FOCV_Function.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // FOCV_Function.hpp 3 | // react-native-fast-opencv 4 | // 5 | // Created by Łukasz Kurant on 06/08/2024. 6 | // 7 | 8 | #ifndef FOCV_Function_hpp 9 | #define FOCV_Function_hpp 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #ifdef __cplusplus 16 | #undef YES 17 | #undef NO 18 | #include 19 | using namespace cv; 20 | #if __has_feature(objc_bool) 21 | #define YES __objc_yes 22 | #define NO __objc_no 23 | #else 24 | #define YES ((BOOL)1) 25 | #define NO ((BOOL)0) 26 | #endif 27 | #endif 28 | 29 | using namespace facebook; 30 | 31 | class FOCV_Function { 32 | public: 33 | static jsi::Object invoke(jsi::Runtime& runtime, const jsi::Value* arguments, size_t count); 34 | }; 35 | 36 | 37 | #endif /* FOCV_Function_hpp */ 38 | -------------------------------------------------------------------------------- /cpp/FOCV_FunctionArguments.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // FOCV_FunctionArguments.hpp 3 | // react-native-fast-opencv 4 | // 5 | // Created by Łukasz Kurant on 20/08/2024. 6 | // 7 | 8 | #ifndef FOCV_FunctionArguments_hpp 9 | #define FOCV_FunctionArguments_hpp 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #ifdef __cplusplus 16 | #undef YES 17 | #undef NO 18 | #include 19 | using namespace cv; 20 | #if __has_feature(objc_bool) 21 | #define YES __objc_yes 22 | #define NO __objc_no 23 | #else 24 | #define YES ((BOOL)1) 25 | #define NO ((BOOL)0) 26 | #endif 27 | #endif 28 | 29 | using namespace facebook; 30 | 31 | class FOCV_FunctionArguments { 32 | private: 33 | const jsi::Value* arguments; 34 | jsi::Runtime* runtime; 35 | size_t count; 36 | 37 | inline void assertIndex(int index) { 38 | if (index < 0 || index >= count) { 39 | throw std::runtime_error("Fast OpenCV Error: Argument index (" + std::to_string(index) + ") is out of bounds!"); 40 | } 41 | } 42 | 43 | public: 44 | FOCV_FunctionArguments(jsi::Runtime& runtime, const jsi::Value* arguments, size_t count); 45 | 46 | double asNumber(int index); 47 | bool asBool(int index); 48 | std::string asString(int index); 49 | std::shared_ptr asMatPtr(int index); 50 | std::shared_ptr> asMatVectorPtr(int index); 51 | std::shared_ptr asPointPtr(int index); 52 | std::shared_ptr> asPointVectorPtr(int index); 53 | std::shared_ptr asPoint2fPtr(int index); 54 | std::shared_ptr> asPoint2fVectorPtr(int index); 55 | std::shared_ptr>> asPointVectorOfVectorsPtr(int index); 56 | std::shared_ptr asRectPtr(int index); 57 | std::shared_ptr> asRectVectorPtr(int index); 58 | std::shared_ptr asSizePtr(int index); 59 | std::shared_ptr asScalarPtr(int index); 60 | std::shared_ptr asRotatedRectPtr(int index); 61 | std::shared_ptr asTermCriteriaPtr(int index); 62 | 63 | bool isNumber(int index); 64 | bool isBool(int index); 65 | bool isString(int index); 66 | bool isObject(int index); 67 | bool isMat(int index); 68 | bool isMatVector(int index); 69 | bool isPoint(int index); 70 | bool isPointVector(int index); 71 | bool isPoint2f(int index); 72 | bool isPoint2fVector(int index); 73 | bool isPointVectorOfVectors(int index); 74 | bool isRect(int index); 75 | bool isRectVector(int index); 76 | bool isSize(int index); 77 | bool isScalar(int index); 78 | bool isRotatedRect(int index); 79 | bool isTermCriteria(int index); 80 | }; 81 | 82 | #endif /* FOCV_FunctionArguments_hpp */ 83 | -------------------------------------------------------------------------------- /cpp/FOCV_Ids.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // FOCV_Ids.cpp 3 | // 4 | // Created by Łukasz Kurant on 04/08/2024. 5 | // 6 | 7 | #include "FOCV_Ids.hpp" 8 | #include "FOCV_JsiObject.hpp" 9 | #include 10 | #include 11 | 12 | using namespace facebook; 13 | 14 | void FOCV_Ids::push(std::string id) { 15 | ids.push_back(id); 16 | } 17 | 18 | jsi::Array FOCV_Ids::toJsiArray(jsi::Runtime& runtime, std::string type) { 19 | auto result = jsi::Array(runtime, ids.size()); 20 | 21 | for (int i = 0; i < ids.size(); i++) { 22 | result.setValueAtIndex(runtime, i, FOCV_JsiObject::wrap(runtime, type, ids[i])); 23 | } 24 | 25 | return result; 26 | } 27 | -------------------------------------------------------------------------------- /cpp/FOCV_Ids.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // FOCV_Ids.hpp 3 | // 4 | // Created by Łukasz Kurant on 04/08/2024. 5 | // 6 | 7 | #ifndef FOCV_Ids_hpp 8 | #define FOCV_Ids_hpp 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | class FOCV_Ids { 15 | private: 16 | std::vector ids; 17 | 18 | public: 19 | void push(std::string id); 20 | facebook::jsi::Array toJsiArray(facebook::jsi::Runtime& runtime, std::string type); 21 | }; 22 | 23 | #endif /* FOCV_Ids_hpp */ 24 | -------------------------------------------------------------------------------- /cpp/FOCV_JsiObject.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // FOCV_JsiObject.cpp 3 | // react-native-fast-opencv 4 | // 5 | // Created by Łukasz Kurant on 10/08/2024. 6 | // 7 | 8 | #include "FOCV_JsiObject.hpp" 9 | 10 | jsi::Object FOCV_JsiObject::wrap(jsi::Runtime& runtime, std::string type, std::string id) { 11 | jsi::Object wrapped(runtime); 12 | wrapped.setProperty(runtime, "type", jsi::String::createFromUtf8(runtime, type)); 13 | wrapped.setProperty(runtime, "id", jsi::String::createFromUtf8(runtime, id)); 14 | return wrapped; 15 | } 16 | 17 | std::string FOCV_JsiObject::id_from_wrap(jsi::Runtime& runtime, const jsi::Value& wrap) { 18 | return wrap.asObject(runtime).getProperty(runtime, "id").asString(runtime).utf8(runtime); 19 | } 20 | 21 | std::string FOCV_JsiObject::type_from_wrap(jsi::Runtime& runtime, const jsi::Value& wrap) { 22 | return wrap.asObject(runtime).getProperty(runtime, "type").asString(runtime).utf8(runtime); 23 | } 24 | -------------------------------------------------------------------------------- /cpp/FOCV_JsiObject.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // FOCV_JsiObject.hpp 3 | // react-native-fast-opencv 4 | // 5 | // Created by Łukasz Kurant on 10/08/2024. 6 | // 7 | 8 | #ifndef FOCV_JsiObject_hpp 9 | #define FOCV_JsiObject_hpp 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | using namespace facebook; 16 | 17 | class FOCV_JsiObject { 18 | public: 19 | static jsi::Object wrap(jsi::Runtime& runtime, std::string type, std::string id); 20 | static std::string id_from_wrap(jsi::Runtime& runtime, const jsi::Value& wrap); 21 | static std::string type_from_wrap(jsi::Runtime& runtime, const jsi::Value& wrap); 22 | }; 23 | 24 | #endif /* FOCV_JsiObject_hpp */ 25 | -------------------------------------------------------------------------------- /cpp/FOCV_Object.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // FOCV_Object.hpp 3 | // Pods 4 | // 5 | // Created by Łukasz Kurant on 06/08/2024. 6 | // 7 | 8 | #ifndef FOCV_Object_hpp 9 | #define FOCV_Object_hpp 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #ifdef __cplusplus 16 | #undef YES 17 | #undef NO 18 | #include 19 | using namespace cv; 20 | #if __has_feature(objc_bool) 21 | #define YES __objc_yes 22 | #define NO __objc_no 23 | #else 24 | #define YES ((BOOL)1) 25 | #define NO ((BOOL)0) 26 | #endif 27 | #endif 28 | 29 | using namespace facebook; 30 | 31 | class FOCV_Object { 32 | public: 33 | static jsi::Object create(jsi::Runtime& runtime, const jsi::Value* arguments, size_t count); 34 | static jsi::Object convertToJSI(jsi::Runtime& runtime, const jsi::Value* arguments, size_t count); 35 | static jsi::Object copyObjectFromVector(jsi::Runtime& runtime, const jsi::Value* arguments, size_t count); 36 | static void addObjectToVector(jsi::Runtime& runtime, const jsi::Value* arguments, size_t count); 37 | }; 38 | 39 | #endif /* FOCV_Object_hpp */ 40 | -------------------------------------------------------------------------------- /cpp/FOCV_Storage.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // FOCV_Storage.cpp 3 | // react-native-fast-opencv 4 | // 5 | // Created by Łukasz Kurant on 05/08/2024. 6 | // 7 | 8 | #include "FOCV_Storage.hpp" 9 | #include 10 | 11 | std::unordered_map FOCV_Storage::items = std::unordered_map(); 12 | 13 | void FOCV_Storage::clear(const std::set &ids_to_keep) { 14 | for (auto it = items.begin(); it != items.end();) { 15 | auto id = it->first; 16 | if (ids_to_keep.find(id) != ids_to_keep.end()) { 17 | it++; 18 | } else { 19 | it = items.erase(it); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /cpp/FOCV_Storage.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // FOCV_Storage.hpp 3 | // react-native-fast-opencv 4 | // 5 | // Created by Łukasz Kurant on 05/08/2024. 6 | // 7 | 8 | #ifndef FOCV_Storage_hpp 9 | #define FOCV_Storage_hpp 10 | 11 | #include 12 | #include 13 | #include 14 | #include "UUID.hpp" 15 | 16 | #ifdef __cplusplus 17 | #include 18 | #endif 19 | 20 | class FOCV_Storage { 21 | public: 22 | private: 23 | static std::unordered_map items; 24 | 25 | public: 26 | template 27 | static std::shared_ptr get(std::string key); 28 | 29 | template 30 | static std::string save(T &item); 31 | 32 | template 33 | static std::string save(std::string key, T &item); 34 | 35 | static void clear(const std::set &ids_to_keep); 36 | }; 37 | 38 | template 39 | std::string FOCV_Storage::save(T &item) { 40 | std::string key = uuid::generate_uuid_v4(); 41 | 42 | items.insert_or_assign(key, std::make_shared(item)); 43 | 44 | return key; 45 | } 46 | 47 | template 48 | std::string FOCV_Storage::save(std::string key, T &item) { 49 | items.insert_or_assign(key, std::make_shared(item)); 50 | 51 | return key; 52 | } 53 | 54 | template 55 | std::shared_ptr FOCV_Storage::get(std::string key) { 56 | if (!items.contains(key)) { 57 | throw std::runtime_error("Fast OpenCV Error: Object with id '" + key + "' not found in storage!"); 58 | } 59 | 60 | return std::any_cast>(items.at(key)); 61 | } 62 | 63 | 64 | #endif /* FOCV_Storage_hpp */ 65 | -------------------------------------------------------------------------------- /cpp/UUID.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // UUID.cpp 3 | // react-native-fast-opencv 4 | // 5 | // Created by Łukasz Kurant on 21/08/2024. 6 | // 7 | 8 | #include "UUID.hpp" 9 | #include 10 | #include 11 | 12 | namespace uuid { 13 | static std::random_device rd; 14 | static std::mt19937 gen(rd()); 15 | static std::uniform_int_distribution<> dis(0, 15); 16 | static std::uniform_int_distribution<> dis2(8, 11); 17 | 18 | std::string generate_uuid_v4() { 19 | std::stringstream ss; 20 | int i; 21 | ss << std::hex; 22 | for (i = 0; i < 8; i++) { 23 | ss << dis(gen); 24 | } 25 | ss << "-"; 26 | for (i = 0; i < 4; i++) { 27 | ss << dis(gen); 28 | } 29 | ss << "-4"; 30 | for (i = 0; i < 3; i++) { 31 | ss << dis(gen); 32 | } 33 | ss << "-"; 34 | ss << dis2(gen); 35 | for (i = 0; i < 3; i++) { 36 | ss << dis(gen); 37 | } 38 | ss << "-"; 39 | for (i = 0; i < 12; i++) { 40 | ss << dis(gen); 41 | }; 42 | return ss.str(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /cpp/UUID.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // UUID.hpp 3 | // react-native-fast-opencv 4 | // 5 | // Created by Łukasz Kurant on 21/08/2024. 6 | // 7 | 8 | #ifndef UUID_hpp 9 | #define UUID_hpp 10 | 11 | #include 12 | #include 13 | 14 | namespace uuid { 15 | std::string generate_uuid_v4(); 16 | } 17 | 18 | #endif /* UUID_hpp */ 19 | -------------------------------------------------------------------------------- /cpp/jsi/Promise.cpp: -------------------------------------------------------------------------------- 1 | // Copied from https://github.com/mrousavy/react-native-fast-tflite/blob/main/cpp/jsi/Promise.cpp 2 | // Credits to Marc Rousavy 3 | 4 | #include "Promise.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace mrousavy { 11 | 12 | using namespace facebook; 13 | 14 | Promise::Promise(jsi::Runtime& runtime, jsi::Value resolver, jsi::Value rejecter) 15 | : runtime(runtime), _resolver(std::move(resolver)), _rejecter(std::move(rejecter)) {} 16 | 17 | jsi::Value Promise::createPromise(jsi::Runtime& runtime, 18 | std::function promise)> run) { 19 | // Get Promise ctor from global 20 | auto promiseCtor = runtime.global().getPropertyAsFunction(runtime, "Promise"); 21 | 22 | auto promiseCallback = jsi::Function::createFromHostFunction( 23 | runtime, jsi::PropNameID::forUtf8(runtime, "PromiseCallback"), 2, 24 | [=](jsi::Runtime& runtime, const jsi::Value& thisValue, const jsi::Value* arguments, 25 | size_t count) -> jsi::Value { 26 | // Call function 27 | auto promise = std::make_shared(runtime, arguments[0].asObject(runtime), 28 | arguments[1].asObject(runtime)); 29 | run(promise); 30 | 31 | return jsi::Value::undefined(); 32 | }); 33 | 34 | return promiseCtor.callAsConstructor(runtime, promiseCallback); 35 | } 36 | 37 | void Promise::resolve(jsi::Value&& result) { 38 | _resolver.asObject(runtime).asFunction(runtime).call(runtime, std::move(result)); 39 | } 40 | 41 | void Promise::reject(std::string message) { 42 | jsi::JSError error(runtime, message); 43 | _rejecter.asObject(runtime).asFunction(runtime).call(runtime, error.value()); 44 | } 45 | 46 | } // namespace mrousavy -------------------------------------------------------------------------------- /cpp/jsi/Promise.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #ifdef ANDROID 8 | #include 9 | #else 10 | #include 11 | #endif 12 | 13 | namespace mrousavy { 14 | 15 | using namespace facebook; 16 | 17 | class Promise { 18 | public: 19 | Promise(jsi::Runtime& runtime, jsi::Value resolver, jsi::Value rejecter); 20 | 21 | void resolve(jsi::Value&& result); 22 | void reject(std::string error); 23 | 24 | public: 25 | jsi::Runtime& runtime; 26 | 27 | private: 28 | jsi::Value _resolver; 29 | jsi::Value _rejecter; 30 | 31 | public: 32 | /** 33 | Create a new Promise and runs the given `run` function. 34 | */ 35 | static jsi::Value createPromise(jsi::Runtime& runtime, 36 | std::function promise)> run); 37 | }; 38 | 39 | } // namespace mrousavy -------------------------------------------------------------------------------- /cpp/jsi/TypedArray.h: -------------------------------------------------------------------------------- 1 | // 2 | // TypedArray.h 3 | // VisionCamera 4 | // 5 | // Created by Marc Rousavy on 21.02.23. 6 | // Copyright © 2023 mrousavy. All rights reserved. 7 | // 8 | 9 | // Copied & Adapted from 10 | // https://github.com/expo/expo/blob/main/packages/expo-gl/common/EXTypedArrayApi.h Credits to Expo 11 | 12 | #pragma once 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | namespace mrousavy { 19 | 20 | using namespace facebook; 21 | 22 | enum class TypedArrayKind { 23 | Int8Array, 24 | Int16Array, 25 | Int32Array, 26 | Uint8Array, 27 | Uint8ClampedArray, 28 | Uint16Array, 29 | Uint32Array, 30 | Float32Array, 31 | Float64Array, 32 | BigInt64Array, 33 | BigUint64Array, 34 | }; 35 | 36 | template class TypedArray; 37 | 38 | template struct typedArrayTypeMap; 39 | template <> struct typedArrayTypeMap { 40 | typedef int8_t type; 41 | }; 42 | template <> struct typedArrayTypeMap { 43 | typedef int16_t type; 44 | }; 45 | template <> struct typedArrayTypeMap { 46 | typedef int32_t type; 47 | }; 48 | template <> struct typedArrayTypeMap { 49 | typedef uint8_t type; 50 | }; 51 | template <> struct typedArrayTypeMap { 52 | typedef uint8_t type; 53 | }; 54 | template <> struct typedArrayTypeMap { 55 | typedef uint16_t type; 56 | }; 57 | template <> struct typedArrayTypeMap { 58 | typedef uint32_t type; 59 | }; 60 | template <> struct typedArrayTypeMap { 61 | typedef float type; 62 | }; 63 | template <> struct typedArrayTypeMap { 64 | typedef double type; 65 | }; 66 | template <> struct typedArrayTypeMap { 67 | typedef int64_t type; 68 | }; 69 | template <> struct typedArrayTypeMap { 70 | typedef uint64_t type; 71 | }; 72 | 73 | // Instance of this class will invalidate PropNameIDCache when destructor is called. 74 | // Attach this object to global in specific jsi::Runtime to make sure lifecycle of 75 | // the cache object is connected to the lifecycle of the js runtime 76 | class InvalidateCacheOnDestroy : public jsi::HostObject { 77 | public: 78 | explicit InvalidateCacheOnDestroy(jsi::Runtime& runtime); 79 | virtual ~InvalidateCacheOnDestroy(); 80 | virtual jsi::Value get(jsi::Runtime&, const jsi::PropNameID& name) { 81 | return jsi::Value::null(); 82 | } 83 | virtual void set(jsi::Runtime&, const jsi::PropNameID& name, const jsi::Value& value) {} 84 | virtual std::vector getPropertyNames(jsi::Runtime& rt) { 85 | return {}; 86 | } 87 | 88 | private: 89 | uintptr_t key; 90 | }; 91 | 92 | class TypedArrayBase : public jsi::Object { 93 | public: 94 | template using ContentType = typename typedArrayTypeMap::type; 95 | 96 | TypedArrayBase(jsi::Runtime&, size_t, TypedArrayKind); 97 | TypedArrayBase(jsi::Runtime&, const jsi::Object&); 98 | TypedArrayBase(TypedArrayBase&&) = default; 99 | TypedArrayBase& operator=(TypedArrayBase&&) = default; 100 | 101 | TypedArrayKind getKind(jsi::Runtime& runtime) const; 102 | 103 | template TypedArray get(jsi::Runtime& runtime) const&; 104 | template TypedArray get(jsi::Runtime& runtime) &&; 105 | template TypedArray as(jsi::Runtime& runtime) const&; 106 | template TypedArray as(jsi::Runtime& runtime) &&; 107 | 108 | size_t size(jsi::Runtime& runtime) const; 109 | size_t length(jsi::Runtime& runtime) const; 110 | size_t byteLength(jsi::Runtime& runtime) const; 111 | size_t byteOffset(jsi::Runtime& runtime) const; 112 | bool hasBuffer(jsi::Runtime& runtime) const; 113 | 114 | std::vector toVector(jsi::Runtime& runtime); 115 | jsi::ArrayBuffer getBuffer(jsi::Runtime& runtime) const; 116 | 117 | private: 118 | template friend class TypedArray; 119 | }; 120 | 121 | bool isTypedArray(jsi::Runtime& runtime, const jsi::Object& jsObj); 122 | TypedArrayBase getTypedArray(jsi::Runtime& runtime, const jsi::Object& jsObj); 123 | 124 | std::vector arrayBufferToVector(jsi::Runtime& runtime, jsi::Object& jsObj); 125 | void arrayBufferUpdate(jsi::Runtime& runtime, jsi::ArrayBuffer& buffer, std::vector data, 126 | size_t offset); 127 | 128 | template class TypedArray : public TypedArrayBase { 129 | public: 130 | explicit TypedArray(TypedArrayBase&& base); 131 | TypedArray(jsi::Runtime& runtime, size_t size); 132 | TypedArray(jsi::Runtime& runtime, ContentType* dataToCopy, size_t size); 133 | TypedArray(jsi::Runtime& runtime, std::vector> data); 134 | TypedArray(TypedArray&&) = default; 135 | TypedArray& operator=(TypedArray&&) = default; 136 | 137 | std::vector> toVector(jsi::Runtime& runtime); 138 | void update(jsi::Runtime& runtime, const std::vector>& data); 139 | void updateUnsafe(jsi::Runtime& runtime, ContentType* data, size_t length); 140 | uint8_t* data(jsi::Runtime& runtime); 141 | }; 142 | 143 | template TypedArray TypedArrayBase::get(jsi::Runtime& runtime) const& { 144 | assert(getKind(runtime) == T); 145 | (void)runtime; // when assert is disabled we need to mark this as used 146 | return TypedArray(jsi::Value(runtime, jsi::Value(runtime, *this).asObject(runtime))); 147 | } 148 | 149 | template TypedArray TypedArrayBase::get(jsi::Runtime& runtime) && { 150 | assert(getKind(runtime) == T); 151 | (void)runtime; // when assert is disabled we need to mark this as used 152 | return TypedArray(std::move(*this)); 153 | } 154 | 155 | template TypedArray TypedArrayBase::as(jsi::Runtime& runtime) const& { 156 | if (getKind(runtime) != T) { 157 | throw jsi::JSError(runtime, "Object is not a TypedArray"); 158 | } 159 | return get(runtime); 160 | } 161 | 162 | template TypedArray TypedArrayBase::as(jsi::Runtime& runtime) && { 163 | if (getKind(runtime) != T) { 164 | throw jsi::JSError(runtime, "Object is not a TypedArray"); 165 | } 166 | return std::move(*this).get(runtime); 167 | } 168 | } // namespace mrousavy -------------------------------------------------------------------------------- /cpp/react-native-fast-opencv.h: -------------------------------------------------------------------------------- 1 | #ifndef FASTOPENCV_H 2 | #define FASTOPENCV_H 3 | 4 | #include 5 | #include 6 | 7 | #ifdef __cplusplus 8 | #undef YES 9 | #undef NO 10 | #include 11 | using namespace cv; 12 | #if __has_feature(objc_bool) 13 | #define YES __objc_yes 14 | #define NO __objc_no 15 | #else 16 | #define YES ((BOOL)1) 17 | #define NO ((BOOL)0) 18 | #endif 19 | #endif 20 | 21 | #ifdef ANDROID 22 | #include 23 | #else 24 | #include 25 | #endif 26 | 27 | using namespace facebook; 28 | 29 | class OpenCVPlugin : public jsi::HostObject { 30 | private: 31 | std::shared_ptr _callInvoker; 32 | 33 | public: 34 | explicit OpenCVPlugin(std::shared_ptr callInvoker); 35 | static void installOpenCV(jsi::Runtime& runtime, std::shared_ptr callInvoker); 36 | 37 | jsi::Value get(jsi::Runtime& runtime, const jsi::PropNameID& name) override; 38 | std::vector getPropertyNames(jsi::Runtime& runtime) override; 39 | }; 40 | 41 | #endif /* FASTOPENCV_H */ 42 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # docs 2 | -------------------------------------------------------------------------------- /docs/images/blur-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukaszkurantdev/react-native-fast-opencv/56c99571718056de4f466d9809913325c2e721e0/docs/images/blur-example.png -------------------------------------------------------------------------------- /docs/images/blur-phones.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukaszkurantdev/react-native-fast-opencv/56c99571718056de4f466d9809913325c2e721e0/docs/images/blur-phones.png -------------------------------------------------------------------------------- /docs/images/realtime-example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukaszkurantdev/react-native-fast-opencv/56c99571718056de4f466d9809913325c2e721e0/docs/images/realtime-example.gif -------------------------------------------------------------------------------- /docs/images/realtime-phones.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukaszkurantdev/react-native-fast-opencv/56c99571718056de4f466d9809913325c2e721e0/docs/images/realtime-phones.png -------------------------------------------------------------------------------- /docs/next.config.js: -------------------------------------------------------------------------------- 1 | const withNextra = require('nextra')({ 2 | theme: 'nextra-theme-docs', 3 | themeConfig: './theme.config.jsx', 4 | }); 5 | 6 | module.exports = { 7 | ...withNextra(), 8 | basePath: '/react-native-fast-opencv', 9 | output: 'export', 10 | images: { unoptimized: true }, 11 | }; 12 | -------------------------------------------------------------------------------- /docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "docs", 3 | "packageManager": "yarn@3.6.4", 4 | "dependencies": { 5 | "next": "^14.2.6", 6 | "nextra": "^2.13.4", 7 | "nextra-theme-docs": "^2.13.4", 8 | "react": "^18.3.1", 9 | "react-dom": "^18.3.1" 10 | }, 11 | "scripts": { 12 | "dev": "next", 13 | "build": "next build", 14 | "start": "next start", 15 | "export": "next build && next export" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /docs/pages/_meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "index": "Introduction", 3 | "installation": "Installation", 4 | "usage": "Usage", 5 | "apidetails": "API", 6 | "availablefunctions": "Available OpenCV functions", 7 | "customized": "Customized calculations (C++)", 8 | "---": { 9 | "type": "separator" 10 | }, 11 | "examples": "Examples" 12 | } 13 | -------------------------------------------------------------------------------- /docs/pages/apidetails.md: -------------------------------------------------------------------------------- 1 | # API 2 | 3 | ## Objects 4 | 5 | ### Create Object 6 | 7 | The function creates a new object of the selected type and stores it in memory. The returned object contains the object identifier and type. 8 | 9 | ```js 10 | createObject( 11 | type: ObjectType.Mat, 12 | rows: number, 13 | cols: number, 14 | dataType: DataTypes, 15 | data?: number[] 16 | ): Mat; 17 | createObject(type: ObjectType.MatVector): MatVector; 18 | createObject(type: ObjectType.Point, x: number, y: number): Point; 19 | createObject(type: ObjectType.PointVector): PointVector; 20 | createObject(type: ObjectType.PointVectorOfVectors): PointVectorOfVectors; 21 | createObject( 22 | type: ObjectType.Rect, 23 | x: number, 24 | y: number, 25 | width: number, 26 | height: number 27 | ): Rect; 28 | createObject(type: ObjectType.RectVector): RectVector; 29 | createObject(type: ObjectType.Size, width: number, height: number): Size; 30 | createObject(type: ObjectType.Scalar, a: number): Scalar; 31 | createObject( 32 | type: ObjectType.Scalar, 33 | a: number, 34 | b: number, 35 | c: number 36 | ): Scalar; 37 | createObject( 38 | type: ObjectType.Scalar, 39 | a: number, 40 | b: number, 41 | c: number, 42 | d: number 43 | ): Scalar; 44 | ``` 45 | 46 | --- 47 | 48 | ### Copy Object from Vector 49 | 50 | Copies an object from a vector to a separate object. 51 | 52 | ```js 53 | copyObjectFromVector(vector: MatVector, itemIndex: number): Mat; 54 | copyObjectFromVector(vector: PointVector, itemIndex: number): Point; 55 | copyObjectFromVector( 56 | vector: PointVectorOfVectors, 57 | itemIndex: number 58 | ): PointVector; 59 | copyObjectFromVector(vector: RectVector, itemIndex: number): Rect; 60 | ``` 61 | 62 | --- 63 | 64 | ### Add Object to Vector 65 | 66 | Adds an object to a vector. 67 | 68 | ```js 69 | addObjectToVector(vector: MatVector, object: Mat): void; 70 | addObjectToVector(vector: PointVector, object: Point): void; 71 | addObjectToVector(vector: RectVector, object: Rect): void; 72 | addObjectToVector(vector: PointVectorOfVectors, object: PointVector): void; 73 | ``` 74 | 75 | --- 76 | 77 | ### To JS Value 78 | 79 | Converts an object to a JS-readable object. 80 | 81 | ```js 82 | toJSValue( 83 | mat: Mat, 84 | outImageFormat?: 'jpeg' | 'png' 85 | ): { 86 | size: number; 87 | cols: number; 88 | rows: number; 89 | base64: string; 90 | }; 91 | toJSValue(matVector: MatVector): { 92 | array: { size: number; cols: number; rows: number }[]; 93 | }; 94 | toJSValue(point: Point): { 95 | x: number; 96 | y: number; 97 | }; 98 | toJSValue(pointVector: PointVector): { 99 | array: { 100 | x: number; 101 | y: number; 102 | }[]; 103 | }; 104 | toJSValue(pointVector: PointVectorOfVectors): { 105 | array: { 106 | x: number; 107 | y: number; 108 | }[][]; 109 | }; 110 | toJSValue(rect: Rect): { 111 | x: number; 112 | y: number; 113 | width: number; 114 | height: number; 115 | }; 116 | toJSValue(rectVector: RectVector): { 117 | array: { 118 | x: number; 119 | y: number; 120 | width: number; 121 | height: number; 122 | }[]; 123 | }; 124 | toJSValue(size: Size): { 125 | width: number; 126 | height: number; 127 | }; 128 | toJSValue(scalar: Scalar): { 129 | a: number; 130 | b?: number; 131 | c?: number; 132 | d?: number; 133 | }; 134 | ``` 135 | 136 | ## Utils 137 | 138 | ### Clear Buffers 139 | 140 | Clears stored objects from memory. 141 | 142 | ```js 143 | clearBuffers(idsToKeep?: string[]): void; 144 | ``` 145 | 146 | ### Buffer to Mat 147 | 148 | Creates an object of type Mat based on an array of Buffer Array. 149 | 150 | ```js 151 | bufferToMat( 152 | type: T, 153 | rows: number, 154 | cols: number, 155 | channels: 1 | 3 | 4, 156 | input: ImportBufferType[T] 157 | ): Mat; 158 | ``` 159 | 160 | where `ImportBufferType` is: 161 | 162 | ```ts 163 | type ImportBufferType = { 164 | uint8: Uint8Array; 165 | uint16: Uint16Array; 166 | int8: Int8Array; 167 | int16: Int16Array; 168 | int32: Int32Array; 169 | float32: Float32Array; 170 | float64: Float64Array; 171 | }; 172 | ``` 173 | 174 | ### Base64 to Mat 175 | 176 | Creates an object of type Mat based on image in Base64. 177 | 178 | ```js 179 | base64ToMat(data: string): Mat; 180 | ``` 181 | 182 | ### Mat to Buffer 183 | 184 | Convert Mat object to Uint8Array or Float32Array based on value of parameter and returns with number of cols, rows and channels. 185 | 186 | ```ts 187 | matToBuffer( 188 | mat: Mat, 189 | type: T 190 | ): { cols: number; rows: number; channels: number; buffer: BufferType[T] }; 191 | ``` 192 | 193 | where `BufferType` is: 194 | 195 | ```ts 196 | type BufferType = { 197 | uint8: Uint8Array; 198 | uint16: Uint16Array; 199 | uint32: Uint32Array; 200 | int8: Int8Array; 201 | int16: Int16Array; 202 | int32: Int32Array; 203 | float32: Float32Array; 204 | float64: Float64Array; 205 | }; 206 | ``` 207 | 208 | ## Functions 209 | 210 | ### Invoke function 211 | 212 | Performs a function with specified parameters. A full list of functions with parameters is available [here](./availablefunctions.md). 213 | 214 | #### Example 215 | 216 | ```js 217 | invoke(name: 'absdiff', src1: Mat, src2: Mat, dst: Mat): void; 218 | ``` 219 | -------------------------------------------------------------------------------- /docs/pages/customized.md: -------------------------------------------------------------------------------- 1 | # Customized calculations (C++) 2 | 3 | > Only in advanced cases. 4 | 5 | In special cases where you will need maximum performance or some functionality is missing from the library, you can easily create your own code in C++ and run it from JS code. 6 | 7 | As the code can be written in C++, the solution will be fully cross-platform and does not need different implementations for iOS and Android. 8 | 9 | In file `cpp/react-native-fast-opencv.cpp` find this lines: 10 | 11 | ```cpp 12 | /// ... 13 | } else if (propName == "clearBuffers") { 14 | return jsi::Function::createFromHostFunction( 15 | runtime, jsi::PropNameID::forAscii(runtime, "clearBuffers"), 1, 16 | [=](jsi::Runtime& runtime, const jsi::Value& thisValue, const jsi::Value* arguments, 17 | size_t count) -> jsi::Value { 18 | 19 | FOCV_Storage::clear(); 20 | return true; 21 | }); 22 | } 23 | 24 | return jsi::HostObject::get(runtime, propNameId); 25 | } 26 | // ... 27 | ``` 28 | 29 | Add your own else check with your own customized name of function: 30 | 31 | ```cpp 32 | else if (propName == "customFunction") { 33 | return jsi::Function::createFromHostFunction( 34 | runtime, jsi::PropNameID::forAscii(runtime, "clearBuffers"), 1, 35 | [=](jsi::Runtime& runtime, const jsi::Value& thisValue, const jsi::Value* arguments, 36 | size_t count) -> jsi::Value { 37 | 38 | // Params 39 | FOCV_FunctionArguments args(runtime, arguments); 40 | 41 | // Example – first param is string 42 | auto param1 = args.asString(0); 43 | 44 | // Second param is Mat 45 | auto functionName = args.asMatPtr(1); 46 | 47 | // etc 48 | 49 | // Calculations... 50 | // HERE OpenCV functions and API is available 51 | // Check how to get parameters from function 52 | // Examples in FOCV_Function.cpp 53 | 54 | // return JSI data or just do something... 55 | }); 56 | } 57 | ``` 58 | 59 | Compile et voilà – you can use it in your JS code: 60 | 61 | ```js 62 | OpenCV.customFunction(params); 63 | ``` -------------------------------------------------------------------------------- /docs/pages/examples/_meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "realtimedetection": "Real-time detection", 3 | "blur": "Blur image on separated thread", 4 | "document": { 5 | "title": "Document detection ↗", 6 | "href": "https://medium.com/@lukasz.kurant/real-time-document-detection-using-the-camera-in-react-native-b0cc0af3bbd9", 7 | "newWindow": true 8 | }, 9 | "passthrough": { 10 | "title": "Camera passthrough ↗", 11 | "href": "https://github.com/lukaszkurantdev/react-native-fast-opencv/blob/main/example/src/examples/CameraPassthrough.tsx", 12 | "newWindow": true 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /docs/pages/examples/blur.md: -------------------------------------------------------------------------------- 1 | # Blur image on separated thread 2 | 3 | In this example, it will show a way to muddle a photo from a photo gallery using another thread and the WorkletsCore library. 4 | 5 | ![Example result](../../images/blur-phones.png) 6 | 7 | ### Requirements 8 | 9 | - We must have the react-native-fast-opencv and react-native-worklets-core libraries installed. 10 | - We have a way to handle image from gallery and get it in Base64 format. For this purpose, I used the [react-native-image-picker](https://github.com/react-native-image-picker/react-native-image-picker) library. 11 | 12 | ### Code 13 | 14 | Let's start with a simple code that allows us to select an image from the gallery in Base64 format. 15 | 16 | ```js 17 | import { useState } from 'react'; 18 | import { Button, SafeAreaView, Text } from 'react-native'; 19 | import { launchImageLibrary, type Asset } from 'react-native-image-picker'; 20 | 21 | export function ImageExample() { 22 | const [photo, setPhoto] = useState(null); 23 | 24 | const getImageFromGallery = async () => { 25 | const result = await launchImageLibrary({ 26 | mediaType: 'photo', 27 | includeBase64: true, 28 | }); 29 | setPhoto(result.assets?.at(0) || null); 30 | }; 31 | 32 | return ( 33 | 34 |