Image crop scaling issue with react-image-crop - javascript

I just starting to learn React. I've been having issues with the react-image-crop package. The document they have was not newbie friendly, I just barely made it work at this point. Now my issue is that the result cropped image is totally different from the user's selection. My thought is that it might be caused by the scaling of the original image when you select the cropping area. I have limited the window size because some people might choose to upload a large image. If you have any experience using this package, please let me know what I could do to fix this issue, thank you.
import "react-image-crop/dist/ReactCrop.css";
import React, { useState, useRef } from "react";
import ReactCrop from "react-image-crop";
export default function ImageUploader(props) {
const [imgSrc, setImgSrc] = useState();
const [crop, setCrop] = useState();
const [originalImg, setOrgImg] = useState(null);
const imgRef = useRef(null);
const handleImage = async (event) => {
setImgSrc(URL.createObjectURL(event.target.files[0]));
};
const getCroppedImg = async (image, pixelCrop) => {
try {
const canvas = document.createElement("canvas");
console.log(crop);
canvas.width = pixelCrop.width;
canvas.height = pixelCrop.height;
const ctx = canvas.getContext("2d");
// Here is what I think where the problem is at:
ctx.drawImage(
image,
pixelCrop.x,
pixelCrop.y,
pixelCrop.width,
pixelCrop.height,
0,
0,
pixelCrop.width,
pixelCrop.height
);
const base64Image = await canvas.toDataURL("image/jpeg", 1);
props.setCurrentImages(pushImage(props.images, base64Image));
console.log(base64Image);
console.log(props.images);
} catch (e) {
console.log(e);
}
};
function pushImage(array, newImage) {
if (array.lengh === 0) return [newImage];
return [...array, newImage];
}
function handleCropButton() {
getCroppedImg(imgRef.current, crop);
props.setUploadImg(false);
}
return (
<div style={{ height: "600px" }}>
<div>
<input type="file" onChange={handleImage} accept="image/*" />
<button onClick={handleCropButton}>Crop</button>
</div>
<ReactCrop
crop={crop}
aspect={1}
onChange={(c) => setCrop(c)}
onComplete={(crop) => setCrop(crop)}
>
<img
src={imgSrc}
alt=""
style={{ height: "600px" }}
onLoad={() => {
setOrgImg({
height: imgRef.current.clientHeight,
width: imgRef.current.clientWidth,
});
}}
ref={imgRef}
/>
</ReactCrop>
</div>
);
}

Related

How to set cropper width and height

I am trying to add an image cropper component to my project, for that using the react-cropper package. But there is a problem that how to add a fixed width and height for cropper box like "width:200px; height:300px;"
import React, { useState } from "react";
import Cropper from "react-cropper";
import "cropperjs/dist/cropper.css";
import "./Demo.css";
type Props = {
LabelName?: string;
};
export const Demo = (props:Props) => {
const {LabelName} = props;
const [image, setImage] = useState("");
const [cropData, setCropData] = useState("#");
const [cropper, setCropper] = useState<any>();
const onChange = (e: any) => {
e.preventDefault();
let files;
if (e.dataTransfer) {
files = e.dataTransfer.files;
} else if (e.target) {
files = e.target.files;
}
const reader = new FileReader();
reader.onload = () => {
setImage(reader.result as any);
};
reader.readAsDataURL(files[0]);
};
const getCropData = () => {
if (typeof cropper !== "undefined") {
setCropData(cropper.getCroppedCanvas().toDataURL());
}
};
return (
<div>
<div style={{ width: "100%" }}>
<input type="file" onChange={onChange} />
<br />
<Cropper
zoomTo={0.5}
initialAspectRatio={1}
preview=".img-preview"
src={image}
viewMode={1}
minCropBoxHeight={10}
minCropBoxWidth={10}
background={false}
responsive={true}
autoCropArea={1}
checkOrientation={false}
onInitialized={(instance) => {
setCropper(instance);
}}
guides={true}
/>
</div>
<br style={{ clear: "both" }} />
</div>
);
};
export default Demo;
Now I can change like this,
But my requirement is to fix that copper size, Please give me a solution to fix this situation.
Size:
minCropBoxWidth: 200,
minCropBoxHeight: 300,
Required aspect ratio:
aspectRatio: 1 / 1.5,
Block change cropBox:
cropBoxResizable: false,

Canvas returns blank image

I have simply a button 'Create screenshot' and live stream 'rtsp-relay'. When that button is cliked it should take a snapshot or screenshot and show that under image, my problem is it shows blank image, i have tried to do this in two ways and both of them show blank image (live stream works fine).
code works fine with a simple drawing on a canvas:
https://codesandbox.io/s/copy-canvas-c5l8et-c5l8et?file=/src/App.js
but when there is live video playing, it shows blank image when 'Create screenshot' button is clicked.
Any idea why ?
two ways i have tried:
1:
import React, { useRef, useEffect, useState } from 'react';
import ReactDOM from 'react-dom';
import { loadPlayer } from 'rtsp-relay/browser';
const StreamVideo = () => {
const canvasRef = useRef(null);
const createScreenshot = () =>
new Promise((resolve) => {
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
context.drawImage(canvasRef.current, 0, 0);
canvas.toBlob((blob) => {
const src = URL.createObjectURL(blob);
console.log('src', src);
const image = new Image();
image.onload = () => resolve(image);
image.src = src;
});
});
const handleButtonClick = () => {
createScreenshot().then((image) => {
document.body.append(image);
});
};
useEffect(() => {
if (!canvasRef.current) throw new Error('Ref is null');
loadPlayer({
url: 'ws://localho.../api/stream',
canvas: canvasRef.current,
});
}, []);
return (
<div style={{ border: '5px solid red' }}>
<canvas ref={canvasRef} style={{ width: '100%', height: '100%' }} />
<button onClick={handleButtonClick}>Create screenshot</button>
</div>
);
};
export default StreamVideo;
2:
import React, { useRef, useEffect, useState } from 'react';
import ReactDOM from 'react-dom';
import { loadPlayer } from 'rtsp-relay/browser';
const StreamVideo = () => {
const canvasRef = useRef(null);
const createScreenshot = () =>
new Promise((resolve) => {
canvasRef.current.toBlob((blob) => {
const src = URL.createObjectURL(blob);
const image = new Image();
image.onload = () => resolve(image);
image.src = src;
});
});
const handleButtonClick = () => {
createScreenshot().then((image) => {
document.body.append(image);
});
};
useEffect(() => {
if (!canvasRef.current) throw new Error('Ref is null');
loadPlayer({
url: 'ws://localh.../api/stream',
canvas: canvasRef.current,
});
}, []);
return (
<div style={{ border: '5px solid red' }}>
<canvas ref={canvasRef} style={{ width: '100%', height: '100%' }} />
<button onClick={handleButtonClick}>Create screenshot</button>
</div>
);
};
export default StreamVideo;
image:

Is there any way to auto crop the face after doing face detection with face-api.js?

I've implemented face-API in my react project which is detecting a single face with detectSingleFace from the picture.
Now I want to move one step further. I want face-api to auto-crop the face after detection. So, I can store it in some server, state or local storage. Is there any way to do so?
Here you can see a screenshot example I want to achieve One side is a picture another side is the auto cropped face(which I want to implement).
Here is my live code link in codesandbox
Below is my code module for face-api
PhotoFaceDetection.js
import React, { useState, useEffect, useRef } from "react";
import * as faceapi from "face-api.js";
import Img from "./assets/mFace.jpg";
import "./styles.css";
const PhotoFaceDetection = () => {
const [initializing, setInitializing] = useState(false);
const [image, setImage] = useState(Img);
const canvasRef = useRef();
const imageRef = useRef();
// I want to store cropped image in this state
const [pic, setPic] = useState();
useEffect(() => {
const loadModels = async () => {
setInitializing(true);
Promise.all([
// models getting from public/model directory
faceapi.nets.tinyFaceDetector.load("/models"),
faceapi.nets.faceLandmark68Net.load("/models"),
faceapi.nets.faceRecognitionNet.load("/models"),
faceapi.nets.faceExpressionNet.load("/models")
])
.then(console.log("success", "/models"))
.then(handleImageClick)
.catch((e) => console.error(e));
};
loadModels();
}, []);
const handleImageClick = async () => {
if (initializing) {
setInitializing(false);
}
canvasRef.current.innerHTML = faceapi.createCanvasFromMedia(
imageRef.current
);
const displaySize = {
width: 500,
height: 350
};
faceapi.matchDimensions(canvasRef.current, displaySize);
const detections = await faceapi.detectSingleFace(
imageRef.current,
new faceapi.TinyFaceDetectorOptions()
);
const resizeDetections = faceapi.resizeResults(detections, displaySize);
canvasRef.current
.getContext("2d")
.clearRect(0, 0, displaySize.width, displaySize.height);
faceapi.draw.drawDetections(canvasRef.current, resizeDetections);
console.log(
`Width ${detections.box._width} and Height ${detections.box._height}`
);
setPic(detections);
console.log(detections);
};
return (
<div className="App">
<span>{initializing ? "Initializing" : "Ready"}</span>
<div className="display-flex justify-content-center">
<img ref={imageRef} src={image} alt="face" crossorigin="anonymous" />
<canvas ref={canvasRef} className="position-absolute" />
</div>
</div>
);
};
export default PhotoFaceDetection;
After doing a lot of R&D I figured it out. For future readers who may face an issue here is the guide.
I've created another function that will get the original image reference and the bounded box dimension i.e. width and height. After that, I've used faceapi method to extract faces and then with the help of the toDataURL method I actually converted it to base64 file which can be rendered to any image src or can be stored anywhere.
This is the function I was explaining above
async function extractFaceFromBox(imageRef, box) {
const regionsToExtract = [
new faceapi.Rect(box.x, box.y, box.width, box.height)
];
let faceImages = await faceapi.extractFaces(imageRef, regionsToExtract);
if (faceImages.length === 0) {
console.log("No face found");
} else {
const outputImage = "";
faceImages.forEach((cnv) => {
outputImage.src = cnv.toDataURL();
setPic(cnv.toDataURL());
});
// setPic(faceImages.toDataUrl);
console.log("face found ");
console.log(pic);
}
}
Then I call the above function inside my main function where I used faceapi face detection tiny model.
extractFaceFromBox(imageRef.current, detections.box);
You can also visit live code here to check complete implementation

crop image in reactjs using html5 canvas

I am trying to crop an image in reactjs without using any library.First the user will upload image and the user will crop the image using raw js and replace the uploaded image.How can i do it without using any library.I want to use the crop component somthing like this in reactjsreference.For me html cansvas seems to be not working how can i do it using html canvas?this is the sandbox link:sandbox
this is the code i did for showing image:
import React, { Component } from "react";
class ReactCrop extends Component {
constructor(props) {
super(props);
this.state = {
file: null,
};
this.handleChange = this.handleChange.bind(this)
}
handleChange(event) {
this.setState({
file: URL.createObjectURL(event.target.files[0])
})
console.log("ok",this.state.file)
}
saveCroped() {
const width = 500;
const height = 300;
const fileName = e.target.files[0].name;
const reader = new FileReader();
reader.readAsDataURL(e.target.files[0]);
reader.onload = event => {
const img = new Image();
img.src = event.target.result;
img.onload = () => {
const elem = document.createElement('canvas');
elem.width = width;
elem.height = height;
const ctx = elem.getContext('2d');
// img.width and img.height will contain the original dimensions
ctx.drawImage(img, 0, 0, width, height);
ctx.canvas.toBlob((blob) => {
const file = new File([blob], fileName, {
type: 'image/jpeg',
lastModified: Date.now()
});
}, 'image/jpeg', 1);
},
reader.onerror = error => console.log(error);
};
}
componentDidMount() {
console.log('componentDidMount colling ...');
}
render() {
return (
<div className="app">
<div style={{width:'450px', margin:'0 auto'}}>
<label htmlFor="file" style={{paddingRight:'80px',marginLeft:'-10px'}}>
<input id="files" type="file" style={{border:'0px',paddingBottom:'12px'}} key={this.state.inputKey}
onChange={this.handleChange} type="file"/>
</label>
{this.state.file === null?
<p></p>
: <img style={{width:'362px',height:'365px',paddingTop:'10px',marginRight:'85px'}} onClick={this.rotate} src={this.state.file}/>
}
</div>
<button type="button" onClick={() => {this.saveCroped()}}>crop</button>
</div>
);
}
}
export default ReactCrop;
Interesting question. Here is a working example: (it wont download in online sandbox, but works fine on independent branches e.g. localhost, see console messages) sandbox
import React, { useRef, useEffect, useState } from "react";
const Crop = function(){
const canvas = useRef();
const file = useRef();
const preview = useRef();
const [imageParams, setImageParams] = useState();
const [context, setContext] = useState();
// Initialize
useEffect(function(){
file.current.onchange = onNewImage;
setContext( canvas.current.getContext('2d') );
},[])
// Draw image on imageParams Change
useEffect(function(){
if(!imageParams) return;
context.drawImage( preview.current, 0, 0, imageParams.width*(700/imageParams.height), 700 )
}, [imageParams])
// Get with and height, replace preview, set params
function onNewImage(){
const newImgUrl = URL.createObjectURL( file.current.files[0] );
const newImage = document.createElement('img');
newImage.setAttribute('src', newImgUrl);
newImage.onload = function(){
const {width, height} = newImage;
const type = file.current.files[0].type
newImage.setAttribute('width', '50px');
preview.current.parentElement.append(newImage);
preview.current.remove();
preview.current = newImage;
setImageParams({ width, height, type });
}
}
// Save image on click
function handleSave(){
canvas.current.toBlob(function(blob){
const anchor = document.createElement('a');
anchor.innerHTML = 'download';
anchor.download = "my_file."+imageParams.type.replace(/^.{1,}\//,'');
anchor.href = (window.webkitURL || window.URL).createObjectURL(blob);
anchor.dataset.downloadurl = [imageParams.type, anchor.download, anchor.href].join(':');
anchor.click();
preview.current.parentElement.append(anchor);
}, imageParams.type);
}
return <>
<div className="input">
<input
ref={file}
type="file"
accept="image/png, image/jpeg" />
<div
style={{width:"100px", display: 'inline-block'}}>
<img
ref={preview}
/>
</div>
<button
onClick={handleSave} >save image</button>
</div>
<div class="canvas">
<canvas
ref={canvas}
width='700px'
height='700px' />
</div>
</>;
}
export default Crop;

How can I take a picture in React web application (not native)

I need to have ability to take a picture with desktop camera or mobile phone camera from my web application localhost:3000/user-camera route component. And, please dont write about any native solutions because I'm not working on mobile app.
I have try with react-camera and react-webcam package but nothing works.
https://www.npmjs.com/package/react-webcam
Import React from 'react'
Import Webcam from 'react-webcam'
const videoConstraints = {
width: 1280,
height: 720,
facingMode: "user"
};
const WebcamCapture = () => {
const webcamRef = React.useRef(null);
const capture = React.useCallback(
() => {
const imageSrc = webcamRef.current.getScreenshot();
},
[webcamRef]
);
return (
<>
<Webcam
audio={false}
height={720}
ref={webcamRef}
screenshotFormat="image/jpeg"
width={1280}
videoConstraints={videoConstraints}
/>
<button onClick={capture}>Capture photo</button>
</>
);
};
So, is there a way to do this by using javascript maybe using navigator, or is there a npm package that works with react. Does anyone have experience with this?
Thanks
Try this simple module I created on the fly just to test this interesting feature:
const camera = function () {
let width = 0;
let height = 0;
const createObjects = function () {
const video = document.createElement('video');
video.id = 'video';
video.width = width;
video.width = height;
video.autoplay = true;
document.body.appendChild(video);
const canvas = document.createElement('canvas');
canvas.id = 'canvas';
canvas.width = width;
canvas.width = height;
document.body.appendChild(canvas);
}
return {
video: null,
context: null,
canvas: null,
startCamera: function (w = 680, h = 480) {
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
width = w;
height = h;
createObjects();
this.video = document.getElementById('video');
this.canvas = document.getElementById('canvas');
this.context = this.canvas.getContext('2d');
(function (video) {
navigator.mediaDevices.getUserMedia({video: true}).then(function (stream) {
video.srcObject = stream;
video.play();
});
})(this.video)
}
},
takeSnapshot: function () {
this.context.drawImage(this.video, 0, 0, width, height);
}
}
}();
export default camera;
To use this module first import it as regular es6 module
import camera from './camera.js'
Then call:
camera.startCamera();
camera.takeSnapshot();
OK. I manage to resolve my issue by using navigator and getting media device from there to use. For anyone else who is trying to make something like this, I want you to know that Chrome doesn't allow you to use this before you set your web app to use secure connection HTTPS. In react for testing use set HTTPS=true&&npm start, so you'll start your react app as https and it will show you camera and you can put it anywhere in your component's html tags.
import { useCallback, useRef, useState } from "react";
import Webcam from "react-webcam";
const videoConstraints = {
width: 640,
height: 480,
facingMode: "user",
};
const WebcamCapture = () => {
const webcamRef = useRef<any>(null);
const [imgSrc, setImgSrc] = useState<any>(null);
const capture = useCallback(() => {
const imageSrc = webcamRef.current.getScreenshot();
setImgSrc(imageSrc);
}, [webcamRef, setImgSrc]);
return (
<>
<Webcam
audio={false}
ref={webcamRef}
screenshotFormat="image/jpeg"
videoConstraints={videoConstraints}
minScreenshotWidth={180}
minScreenshotHeight={180}
/>
<button onClick={capture}>Capture Photo</button>
{imgSrc && <img src={imgSrc} alt="img" />}
</>
);
};

Categories