Code Monkey home page Code Monkey logo

vision-camera-cropper's Introduction

vision-camera-cropper

A react native vision camera frame processor for cropping

react-native-vision-camera-cropper.mp4

Versions

For Vision Camera v3, use versions 0.x.

For Vision Camera v4, use versions >= 1.0.0.

Installation

yarn add vision-camera-cropper
cd ios && pod install

Add the plugin to your babel.config.js:

module.exports = {
   plugins: [['react-native-worklets-core/plugin']],
    // ...

Note: You have to restart metro-bundler for changes in the babel.config.js file to take effect.

Usage

  1. Crop a frame and return its base64 or path.

    import { crop } from 'vision-camera-cropper';
    
    // ...
    const frameProcessor = useFrameProcessor((frame) => {
      'worklet';
      //coordinates in percentage
      const cropRegion = {
        left:10,
        top:10,
        width:80,
        height:30
      }
      const result = crop(frame,{cropRegion:cropRegion,includeImageBase64:true,saveAsFile:false});
      console.log(result.base64);
    }, []);
  2. Rotate an image.

    const rotated = await rotateImage(base64,degree);

Get Bitmap/UIImage via Reflection

If you are developing a plugin to get the camera frames, you can use reflection to get it as Bitmap or UIImage on the native side.

Java:

Class cls = Class.forName("com.visioncameracropper.CropperFrameProcessorPlugin");
Method m = cls.getMethod("getBitmap",null);
Bitmap bitmap = (Bitmap) m.invoke(null, null);

Objective-C:

- (UIImage*)getUIImage{
    UIImage *image = ((UIImage* (*)(id, SEL))objc_msgSend)(objc_getClass("CropperFrameProcessorPlugin"), sel_registerName("getBitmap"));
    return image;
}

You have to pass {saveBitmap: true} for the crop function.

Blog

How to Create a React Native Vision Camera Plugin to Crop Frames

Contributing

See the contributing guide to learn how to contribute to the repository and the development workflow.

License

MIT


Made with create-react-native-library

vision-camera-cropper's People

Contributors

xulihang avatar tony-xlh avatar

Stargazers

Evan Liu avatar  avatar Stas Shakirov avatar Yunus Emre Acar avatar Meksi Abdennour avatar CarryOneOk avatar Yonatan Bergman avatar Omar Ramírez avatar Steve avatar Sajjad avatar Benjamin Diedrichsen avatar Leonardo Cavalcante avatar Joaquin Zepeda avatar  avatar BILLY SIN avatar  avatar Ricardo Guiliani avatar Teivienn avatar Muhammad Dindi Hidayatullah avatar Anthony jay Ansit avatar  avatar Efstathios Ntonas avatar Kailash Uniyal avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

vision-camera-cropper's Issues

Frame Processor Error: crop is not a function (it is undefined), js engine: VisionCamera

the following error show i have tried every thing from the previous explained issue nothing works , upgraded my react native from 0.72.6 to 0.75.2 and tried every other alternative it didnt worked

package.json file:
{
"name": "fleetmanagement",
"version": "0.0.1",
"private": true,
"scripts": {
"android": "react-native run-android",
"ios": "react-native run-ios",
"lint": "eslint .",
"start": "react-native start",
"test": "jest"
},
"dependencies": {
"@react-native-async-storage/async-storage": "^1.23.1",
"@react-native-masked-view/masked-view": "^0.3.0",
"@react-navigation/bottom-tabs": "^6.5.20",
"@react-navigation/native": "^6.1.17",
"@react-navigation/native-stack": "^6.9.26",
"@react-navigation/stack": "^6.3.20",
"@rneui/base": "^4.0.0-rc.7",
"@rneui/themed": "^4.0.0-rc.8",
"date-fns": "^2.30.0",
"moment": "^2.30.1",
"moment-range": "^4.0.2",
"mrz": "^4.2.0",
"react": "18.3.1",
"react-native": "0.75.2",
"react-native-android-location-services-dialog-box": "^2.8.2",
"react-native-autocomplete-dropdown": "^3.1.5",
"react-native-autocomplete-input": "^5.4.0",
"react-native-blob-util": "^0.19.2",
"react-native-canvas": "^0.1.39",
"react-native-canvas-signature": "^1.0.2",
"react-native-date-picker": "^4.3.3",
"react-native-dropdown-picker": "^5.4.6",
"react-native-eject": "^0.2.0",
"react-native-geolocation-service": "^5.3.1",
"react-native-gesture-handler": "^2.18.1",
"react-native-image-picker": "^7.1.2",
"react-native-maps": "^1.14.0",
"react-native-onesignal": "^5.2.0",
"react-native-paper": "^5.11.4",
"react-native-rename": "^3.2.14",
"react-native-safe-area-context": "^4.10.1",
"react-native-screens": "^3.27.0",
"react-native-share": "^9.4.1",
"react-native-signature-canvas": "^4.7.1",
"react-native-simple-toast": "^3.0.2",
"react-native-svg": "^14.2.0",
"react-native-swipe-list-view": "^3.2.9",
"react-native-table-component": "^1.2.2",
"react-native-toast-message": "^2.1.7",
"react-native-vector-icons": "^10.0.3",
"react-native-vision-camera": "^4.5.2",
"react-native-webview": "^13.6.4",
"react-native-worklets-core": "1.1.0",
"vision-camera-cropper": "^1.0.0",
"vision-camera-dynamsoft-label-recognizer": "^2.1.1"
},
"devDependencies": {
"@babel/core": "^7.20.0",
"@babel/preset-env": "^7.20.0",
"@babel/runtime": "^7.20.0",
"@react-native/babel-preset": "0.75.2",
"@react-native/eslint-config": "^0.75.2",
"@react-native/metro-config": "^0.75.2",
"@react-native/typescript-config": "0.75.2",
"@types/react": "^18.2.6",
"@types/react-native-table-component": "^1.2.8",
"@types/react-test-renderer": "^18.0.0",
"babel-jest": "^29.6.3",
"eslint": "^8.19.0",
"jest": "^29.6.3",
"metro-react-native-babel-preset": "0.76.8",
"prettier": "^2.8.8",
"react-test-renderer": "18.3.1",
"typescript": "5.0.4"
},
"engines": {
"node": ">=18"
},
"packageManager": "[email protected]"
}

babel.config,js:

module.exports = {
presets: ['module:@react-native/babel-preset'],
plugins: [['react-native-worklets-core/plugin']],
};

index.tsx // for using this library
import { useEffect, useState } from "react";
import { View, Text, StyleSheet, Pressable, Platform } from "react-native";
import Svg, { Rect } from "react-native-svg";
import { Camera, useCameraDevice, useCameraFormat, useFrameProcessor } from "react-native-vision-camera";
import { Worklets, useSharedValue } from "react-native-worklets-core";
import { CropRegion, crop } from "vision-camera-cropper";

export interface CameraScreenProps {
route:any;
navigation:any;
}

export default function CameraScreen(props:CameraScreenProps){
const [hasPermission, setHasPermission] = useState(false);
const [isActive,setIsActive] = useState(true);
const device = useCameraDevice("back");
const format = useCameraFormat(device, [
{ videoResolution: { width: 1920, height: 1080 } },
{ fps: 30 }
])
const [cropRegion,setCropRegion] = useState({
left: 10,
top: 20,
width: 80,
height: 30
});
const cropRegionShared = useSharedValue<undefined|CropRegion>(undefined);
const shouldTake = useSharedValue(false);
const [pressed,setPressed] = useState(false);
const capture = () => {
shouldTake.value=true;
}

const adaptCropRegionForIDCard = () => {
let size = getFrameSize();
let regionWidth = 0.8size.width;
let desiredRegionHeight = regionWidth/(85.6/54);
let height = Math.ceil(desiredRegionHeight/size.height
100);
let region = {
left:10,
width:80,
top:20,
height:height
};
setCropRegion(region);
cropRegionShared.value = region;
}

const getViewBox = () => {
const frameSize = getFrameSize();
const viewBox = "0 0 "+frameSize.width+" "+frameSize.height;
return viewBox;
}

const getFrameSize = ():{width:number,height:number} => {
let size = {width:1080,height:1920};
return size;
}

const onCaptured = (base64:string) => {
setIsActive(false);
if (props) {
if (props.navigation) {
props.navigation.navigate({
name: 'Card',
params: { base64: base64 },
merge: true,
});
}
}
}

const onCapturedJS = Worklets.createRunOnJS(onCaptured);

const frameProcessor = useFrameProcessor((frame) => {
'worklet';

if (shouldTake.value && cropRegionShared.value) {
  shouldTake.value = false;

  const result = crop(frame, {
    cropRegion: cropRegionShared.value, 
    includeImageBase64: true, 
    saveAsFile: false
  });
  
  if (result?.base64) {
    onCapturedJS(result.base64);
  }
}

}, []);

useEffect(() => {
(async () => {
const status = await Camera.requestCameraPermission();
setHasPermission(status === 'granted');
setIsActive(true);
adaptCropRegionForIDCard();
})();
}, []);

return (

{device != null &&
hasPermission && (
<>




</>
)}

<Pressable
onPressIn={()=>{setPressed(true)}}
onPressOut={()=>{setPressed(false)}}
onPress={()=>{capture()}}>

<View style={[styles.innerCircle, pressed ? styles.circlePressed:null]}>




)
}

const styles = StyleSheet.create({
bottomBar:{
position: "absolute",
width: "100%",
bottom: 0,
height: 60,
flexDirection:"row",
justifyContent:"center",
},
outerCircle: {
width: 60,
height: 60,
borderRadius: 60 / 2,
backgroundColor: "lightgray",
display:"flex",
alignItems:"center",
justifyContent:"center",
},
innerCircle: {
width: 45,
height: 45,
borderRadius: 45 / 2,
backgroundColor: "white",
},
circlePressed: {
backgroundColor: "lightgray",
}
});

passing non integer values to cropRegion ignores the crop setting (ios, maybe android as well)

Was passing cropRegion.left = 14.5 (non integer) then value was ignored when cropping . Full width of image was taken
I think this can be easly solved by adding Math.round inlines below. Do not think adding it there will impact performance but it can cause unexpected rounding ...
vision-camera-cropper/src/index.tsx

      cropRegionRecord["left"] = config.cropRegion.left;
      cropRegionRecord["top"] = config.cropRegion.top;
      cropRegionRecord["width"] = config.cropRegion.width;
      cropRegionRecord["height"] = config.cropRegion.height;

Ability to specify the filename when saving in crop()

Hi, would be nice if its possible to set Ability to specify the filename when saving in crop()

export function crop(frame: Frame, config?: CropConfig | undefined): CropResult
export interface CropConfig{
  cropRegion?: CropRegion;
  includeImageBase64?: boolean;
  saveBitmap?: boolean;
  saveAsFile?: boolean;
  fileName?: string;
}

Thanks so much

How to run the sample?

I am getting different issues when running the sample. The latest of which is:

[0/2] Re-checking globbed directories...
  [1/2] Re-running CMake...
  -- Configuring done
  -- Generating done
  -- Build files have been written to: C:/Projects/vision-camera-cropper-main/vision-camera-cropper-main/example/node_modules/react-native-worklets-core/android/.cxx/Debug/545y3n5l/armeabi-v7a

  C++ build system [build] failed while executing:
      @echo off
      "C:\\Users\\John Ernest\\AppData\\Local\\Android\\Sdk\\cmake\\3.22.1\\bin\\ninja.exe" ^
        -C ^
        "C:\\Projects\\vision-camera-cropper-main\\vision-camera-cropper-main\\example\\node_modules\\react-native-worklets-core\\android\\.cxx\\Debug\\545y3n5l\\armeabi-v7a" ^
        rnworklets
    from C:\Projects\vision-camera-cropper-main\vision-camera-cropper-main\example\node_modules\react-native-worklets-core\android
  ninja: error: manifest 'build.ninja' still dirty after 100 tries


* Try:
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.
> Get more help at https://help.gradle.org.

BUILD FAILED in 56s

Not building in react-native-vision-camera 4.5.0

Task :vision-camera-cropper:compileDebugJavaWithJavac FAILED
/Users/admin/Documents/Projects/Project/Project-mobile/node_modules/vision-camera-cropper/android/src/main/java/com/visioncameracropper/BitmapUtils.java:36: error: package com.mrousavy.camera.frameprocessor does not exist
import com.mrousavy.camera.frameprocessor.Frame;
^
/Users/admin/Documents/Projects/Project/Project-mobile/node_modules/vision-camera-cropper/android/src/main/java/com/visioncameracropper/BitmapUtils.java:37: error: package com.mrousavy.camera.types does not exist
import com.mrousavy.camera.types.Orientation;
^
/Users/admin/Documents/Projects/Project/Project-mobile/node_modules/vision-camera-cropper/android/src/main/java/com/visioncameracropper/BitmapUtils.java:74: error: cannot find symbol
public static Bitmap getBitmap(Frame image) throws FrameInvalidError {
^
symbol: class Frame
location: class BitmapUtils
/Users/admin/Documents/Projects/Project/Project-mobile/node_modules/vision-camera-cropper/android/src/main/java/com/visioncameracropper/BitmapUtils.java:87: error: cannot find symbol
public static int getRotationDegreeFromOrientation(Orientation orientation) {
^
symbol: class Orientation
location: class BitmapUtils
/Users/admin/Documents/Projects/Project/Project-mobile/node_modules/vision-camera-cropper/android/src/main/java/com/visioncameracropper/VisionCameraCropperPackage.java:9: error: package com.mrousavy.camera.frameprocessor does not exist
import com.mrousavy.camera.frameprocessor.FrameProcessorPluginRegistry;
^
/Users/admin/Documents/Projects/Project/Project-mobile/node_modules/vision-camera-cropper/android/src/main/java/com/visioncameracropper/CropperFrameProcessorPlugin.java:9: error: package com.mrousavy.camera.frameprocessor does not exist
import com.mrousavy.camera.frameprocessor.Frame;
^
/Users/admin/Documents/Projects/Project/Project-mobile/node_modules/vision-camera-cropper/android/src/main/java/com/visioncameracropper/CropperFrameProcessorPlugin.java:10: error: package com.mrousavy.camera.frameprocessor does not exist
import com.mrousavy.camera.frameprocessor.FrameProcessorPlugin;
^
/Users/admin/Documents/Projects/Project/Project-mobile/node_modules/vision-camera-cropper/android/src/main/java/com/visioncameracropper/CropperFrameProcessorPlugin.java:11: error: package com.mrousavy.camera.frameprocessor does not exist
import com.mrousavy.camera.frameprocessor.VisionCameraProxy;
^
/Users/admin/Documents/Projects/Project/Project-mobile/node_modules/vision-camera-cropper/android/src/main/java/com/visioncameracropper/CropperFrameProcessorPlugin.java:19: error: cannot find symbol
public class CropperFrameProcessorPlugin extends FrameProcessorPlugin {
^
symbol: class FrameProcessorPlugin
/Users/admin/Documents/Projects/Project/Project-mobile/node_modules/vision-camera-cropper/android/src/main/java/com/visioncameracropper/CropperFrameProcessorPlugin.java:20: error: cannot find symbol
CropperFrameProcessorPlugin(@nonnull VisionCameraProxy proxy, @nullable Map<String, Object> options) {super();}
^
symbol: class VisionCameraProxy
location: class CropperFrameProcessorPlugin
/Users/admin/Documents/Projects/Project/Project-mobile/node_modules/vision-camera-cropper/android/src/main/java/com/visioncameracropper/CropperFrameProcessorPlugin.java:24: error: cannot find symbol
public Object callback(@nonnull Frame frame, @nullable Map<String, Object> arguments) {
^
symbol: class Frame
location: class CropperFrameProcessorPlugin
/Users/admin/Documents/Projects/Project/Project-mobile/node_modules/vision-camera-cropper/android/src/main/java/com/visioncameracropper/BitmapUtils.java:88: error: package Orientation does not exist
if (orientation.getUnionValue().equals(Orientation.PORTRAIT.getUnionValue())) {
^
/Users/admin/Documents/Projects/Project/Project-mobile/node_modules/vision-camera-cropper/android/src/main/java/com/visioncameracropper/BitmapUtils.java:90: error: package Orientation does not exist
}else if (orientation.getUnionValue().equals(Orientation.LANDSCAPE_LEFT.getUnionValue())) {
^
/Users/admin/Documents/Projects/Project/Project-mobile/node_modules/vision-camera-cropper/android/src/main/java/com/visioncameracropper/BitmapUtils.java:92: error: package Orientation does not exist
} else if (orientation.getUnionValue().equals(Orientation.LANDSCAPE_RIGHT.getUnionValue())) {
^
/Users/admin/Documents/Projects/Project/Project-mobile/node_modules/vision-camera-cropper/android/src/main/java/com/visioncameracropper/BitmapUtils.java:94: error: package Orientation does not exist
}else if (orientation.getUnionValue().equals(Orientation.PORTRAIT_UPSIDE_DOWN.getUnionValue())) {
^
/Users/admin/Documents/Projects/Project/Project-mobile/node_modules/vision-camera-cropper/android/src/main/java/com/visioncameracropper/VisionCameraCropperPackage.java:17: error: cannot find symbol
FrameProcessorPluginRegistry.addFrameProcessorPlugin("crop", CropperFrameProcessorPlugin::new);
^
symbol: variable FrameProcessorPluginRegistry
location: class VisionCameraCropperPackage
/Users/admin/Documents/Projects/Project/Project-mobile/node_modules/vision-camera-cropper/android/src/main/java/com/visioncameracropper/CropperFrameProcessorPlugin.java:23: error: method does not override or implement a method from a supertype
@OverRide
^
Note: /Users/admin/Documents/Projects/Project/Project-mobile/node_modules/vision-camera-cropper/android/src/main/java/com/visioncameracropper/VisionCameraCropperModule.java uses or overrides a deprecated API.
Note: Recompile with -Xlint:deprecation for details.
Note: /Users/admin/Documents/Projects/Project/Project-mobile/node_modules/vision-camera-cropper/android/src/main/java/com/visioncameracropper/CropperFrameProcessorPlugin.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.

region is ignored if not using integers

It was hard to figure out what is wrong

steps to reproduce

      region = {
        left:15.5,
        width:70,
        top:10,
        height:height
      };
  instead of 
        left:15,
        width:70,
        top:10,
        height:height
      };
      ```

Rotatation, crop in pixels, not %

Hi, would be nice if its possible to set height and width in pixels, not %.

Also, is it possible to add rotation prop before frame is cropped?

fix: Make the app build on iOS properly

Hi! 👋

Firstly, thanks for your work on this project! 🙂

I wasn't able to build the app with these changes. Looks like, VisionCameraCropper-Swift.h does not exist in the project. I have removed it and changed few other things that fixed the issue for me.

Here is the change #2

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.