How to Blur Faces in Photos Using React

How to Blur Faces in Photos Using ReactA step-by-step tutorial to give your users some privacyMatthew EnubujeBlockedUnblockFollowFollowingJun 7Repo: https://github.

com/shango44/blur-faces-using-reactThe action of blurring faces in images/video is known as face redaction and it’s important in the age of GDPR/privacy.

This tutorial should be easy to follow as it uses create-react-app and code is explained/commented.

Let’s get straight into it!1.

SetupCreate React Appnpx create-react-app blur-facesWe will be using create-react-app to generate a React project.

Install dependenciescd blur-facesnpm install bootstrap reactstrap react-input-range canvas facesoft –saveReactstrap: React Bootstrap 4 ComponentsReact Input Range: Range SliderCanvas: Canvas implementation for Node JSFacesoft: Face detection APIClearDelete files “app.

test.

js”, “logo.

svg” and “serviceWorker.

js” in the src folder.

Amend the files below:index.

js — Import bootstrap & remove serviceworkerimport React from 'react';import ReactDOM from 'react-dom';import 'bootstrap/dist/css/bootstrap.

min.

css';import '.

/index.

css';import App from '.

/App';ReactDOM.

render(<App />, document.

getElementById('root'));App.

css — Remove some classes.

App { text-align: center;}.

App-header { background-color: #ffffff; flex-direction: column; align-items: center; justify-content: center; font-size: calc(10px + 2vmin); color: #000; padding: 20px 0px;}App.

js — Default React componentimport React, { Component } from 'react';import '.

/App.

css';export default class App extends Component { render() { return ( <div className="App"> </div> ) }}2.

App.

js SetupThis file is where we control the state of the blur system and render the components.

Import dependenciesAdd the code below to the imports.

import Facesoft from 'facesoft';import { Container, Row, Col, Button, Input, Label } from 'reactstrap';import InputRange from 'react-input-range';import 'react-input-range/lib/css/index.

css';Constructorconstructor(props) { super(props); this.

state = { threshold: 9, image: {}, data: [], smooth: true, } this.

facesoft = new Facesoft("INSERT_API_KEY");}In our state:Threshold: Control the blur (0 = none | 10 = fully blurred)Image: Store the uploaded image URI, width and heightData: Store the face detection API dataSmooth: If false, then we will pixelate rather than blurClick here to get a free face detection API Key (API key can be found in the dashboard when you log in).

Renderrender() { const { image, threshold, data, smooth } = this.

state return ( <div className="App"> <header className="App-header"> <h1>Blur Faces In Photos Using React.

js</h1> </header><Container> <Row className="justify-content-md-center"> <Col md="1"> <strong>Smooth</strong> </Col> <Col md="3"> <Label> <Input type="checkbox" id="cb-4" checked={smooth} onChange={e => this.

setState( { smooth: e.

target.

checked } )} /> (Will be pixelated if unchecked) </Label> </Col> <Col md="1"> <strong>Threshold</strong> </Col> <Col md="7"> <InputRange step={0.

25} maxValue={10} minValue={0} value={threshold} onChange={threshold => this.

setState({ threshold })} /> </Col> <Col md="12" style={{paddingTop: 20}}> <hr></hr> </Col> </Row> <Row> <Col md="6"> <div className="uploaded-image"> <input type="file" accept="image/x-png,image/gif,image/jpeg" /> { image.

hasOwnProperty("uri") && <img src={image.

uri} alt="upload" style={{maxWidth: "100%", margin: "10px 0px"}} /> } </div> { image.

hasOwnProperty("uri") && <Button outline color="primary" size="lg" > Blur Faces </Button> } </Col> <Col md="6"></Col> </Row> </Container> </div> )}The code above will add the inputs and text for good UI, and you should now see the following below if you execute “npm start”.

3.

App.

js FunctionsNow, we’re going to add two functions.

One is for changing and setting the image upload, and one is for detecting and retrieving faces in the uploaded photo.

Handle changeAdd this change event after the constructor.

We will assign this to our file input next so we can retrieve the image.

handleChange(event) { if(event.

target.

files.

length < 1) return const scope = this; const uri = URL.

createObjectURL(event.

target.

files[0]); const img = new Image(); img.

src = uri; img.

onload = () => { scope.

setState({ image: { uri, width: img.

width, height: img.

height } }) }}In our input, let’s assign the handle change event to onChange.

<input onChange={this.

handleChange} // ADD THIS type="file" accept="image/x-png,image/gif,image/jpeg"/>Handle clickIn this function, we will be using the Facesoft API passing in the image URI from the file input to get the face axis and dimensions so we can manipulate it.

handleClick(event) { this.

facesoft.

detectFromURL(this.

state.

image.

uri) .

then(result => this.

setState({data: result})) .

catch(error => console.

log(error))}We will assign the handle click function to the onClick method of our Blur Faces button.

<Button onClick={this.

handleClick} // ADD THIS outline color="primary" size="lg"> Blur Faces</Button>BindingBefore we head to the actual blurring mechanism, let us bind the functions so we can access our API and setState.

Add to the constructor.

this.

handleChange = this.

handleChange.

bind(this);this.

handleClick = this.

handleClick.

bind(this);4.

Blurring!What we’ll be doing in this step is creating another file where we pass the state in App.

js to, so we can blur the faces in the photo and then return a canvas.

blurFaces.

jsIn the src folder, create a new file called blurFaces.

js and add the code below.

import React, { Component } from 'react';import { createCanvas, loadImage } from 'canvas';export default class BlurFaces extends Component { componentDidUpdate(prevProps) { } render() { const { width, height } = this.

props.

image; return ( <div> <p><strong>Output</strong></p> <canvas ref="canvas" width={width} height={height} style={{maxWidth: "100%", maxHeight: "auto"}} /> </div> ) }BlurFaces.

defaultProps = { image: { uri: "", width: 0, height: 0 }, threshold: 0, data: [], smooth: true}The code above imports createCanvas and loadImage, which are necessary because our blurring/pixelating technique will involve using multiple hidden canvases.

On render, we return a canvas and we’ve referenced our canvas so we can access it in componentDidUpdate which is were the blurring code will be added.

The default props are there so errors don’t occur.

Get Faces methodAs the data from the face detection API returns face coordinates based on its upper left and lower right, we can map it into x, y, width and height so things get much easier later on.

Add the code below to the class.

getFaces(data) { return data.

map(face => ({ x: face.

upperLeft.

x, y: face.

upperLeft.

y, w: face.

lowerRight.

x – face.

upperLeft.

x, h: face.

lowerRight.

y – face.

upperLeft.

y }))}Component Did UpdateLet’s head to the componentDidUpdate and start creating variables and statements.

The statements are there to detect if no face detection data exists and if the user hasn’t clicked the blur button.

componentDidUpdate(prevProps) { const { image, threshold, data, smooth } = this.

props; // If no data if(data.

length < 1) return; // Output Canvas and Context const outputCanvas = this.

refs.

canvas; const outputCtx = outputCanvas.

getContext('2d'); // Hidden Canvas and Context const hiddenCanvas = createCanvas(image.

width, image.

height); const hiddenCtx = hiddenCanvas.

getContext('2d'); // If data, threshold and smooth is the same then clear and return (user has not clicked blur) if( JSON.

stringify(prevProps.

data) === JSON.

stringify(data) && prevProps.

threshold === threshold && prevProps.

smooth === smooth ) { outputCtx.

clearRect(0,0, image.

width, image.

height); return; } // NEXT CODE WILL BE ADDED HERE}Load ImageWe will be using the loadImage method and pass in our image URI to access the uploaded photo.

// Load ImageloadImage(image.

uri).

then((newImage) => { // NEXT CODE WILL BE ADDED HERE}).

catch(err => { console.

log(err)})BLURSteps:Create a canvas of the full image blurred according to the thresholdExtract the faces from the blurred image using coordinates from API dataCreate a blank canvas and add the blurred faces to itCreate a new canvas and draw the canvas of the blurred faces to apply feathering techniquesClear the visible canvas and draw the uploaded image onto itDraw the canvas of the blurred & feathered faces on top of the visible canvasWe’ll have an if statement to detect if the smooth checkbox was ticked or not.

Then we’ll initialize two canvases for creating inverted masks of blurred images (for feathering).

We will also change the global composite operation to the destination to create feathering (see globalCompositeOperation).

if(smooth){ // New canvases for applying blurring and feathering (canvases for inverted mask of blurred images) const imaskCanvas = createCanvas(image.

width, image.

height); const imaskCtx = imaskCanvas.

getContext('2d'); const imaskCanvas2 = createCanvas(image.

width, image.

height); const imaskCtx2 = imaskCanvas2.

getContext('2d'); // Set global composite operation to destination in imaskCtx.

globalCompositeOperation = "destination-in"; // NEXT CODE WILL BE ADDED HERE}Drawing blurred faces to blank canvasHere we’ll be utilizing two canvases — one for having uploaded image but blurred according to the threshold, and the other a blank canvas where we add the blurred faces.

For each loop, we’ll amend the threshold, because face data with a small width won’t blur well with a high pixel amount and larger widths require more amount of blur.

Also, when putting the image data onto the blank canvas, we amend the axis and dimensions due to feathering.

this.

getFaces(data).

forEach((face, i) => { // Determine the blur amount by width of face let blurAmount = threshold if(face.

w >= 300) blurAmount = threshold*2.

5 else if(face.

w <= 30) blurAmount = threshold*0.

25 // Add blur filter hiddenCtx.

filter = `blur(${blurAmount}px)`; // Draw original image to hidden canvas hiddenCtx.

drawImage(newImage, 0, 0, image.

width, image.

height); // Add blurred faces to blank canvas imaskCtx.

putImageData(hiddenCtx.

getImageData( face.

x-10, face.

y-10, face.

w+20, face.

h+20), face.

x-10, face.

y-10 ) })// NEXT CODE WILL BE ADDED HERECreating feathering, then display to visible canvasBy using shadow blur, then drawing the canvas of blurred faces onto a new canvas and duplicating the process, it creates feathering.

// Draw blurred faces onto 2nd inverted mask canvas imaskCtx2.

drawImage(imaskCanvas, 0, 0); imaskCtx2.

shadowColor = "black"; // Required for feathering imaskCtx2.

shadowBlur = 30; imaskCtx2.

globalCompositeOperation = "destination-in"; // Feathering imaskCtx2.

shadowBlur = 20; imaskCtx2.

drawImage(imaskCanvas,0,0); imaskCtx2.

shadowBlur = 10; imaskCtx2.

drawImage(imaskCanvas,0,0); // Clear visible canvas then draw original image to it and then add the blurred images outputCtx.

clearRect(0,0, image.

width, image.

height); outputCtx.

drawImage(newImage, 0, 0); outputCtx.

drawImage(imaskCanvas2, 0, 0);} // NEXT CODE WILL BE ADDED HEREPixelateApply pixelation styling for the hidden canvas.

Disable smoothing for the hidden canvas context.

Calculate the scaled width and height by threshold (higher threshold = smaller width/height).

Draw the uploaded image onto the hidden canvas with the scaled width and height.

Stretch the scaled image to match the actual image width and height.

Clear the visible canvas and draw the uploaded image onto it.

For each loop, extract faces from pixelated canvas and draw it on top of the visible canvas.

Let’s start by changing the styling and modifying the context options.

else { hiddenCanvas.

style.

cssText = 'image-rendering: optimizeSpeed;' + 'image-rendering: -moz-crisp-edges;' + // FireFox 'image-rendering: -o-crisp-edges;' + // Opera 'image-rendering: -webkit-crisp-edges;' + // Chrome 'image-rendering: crisp-edges;' + // Chrome 'image-rendering: -webkit-optimize-contrast;' + // Safari 'image-rendering: pixelated; ' + // Future browsers '-ms-interpolation-mode: nearest-neighbor;'; // IE // Use nearest-neighbor scaling when images are resized instead of the resizing algorithm to create blur hiddenCtx.

webkitImageSmoothingEnabled = false; hiddenCtx.

mozImageSmoothingEnabled = false; hiddenCtx.

msImageSmoothingEnabled = false; hiddenCtx.

imageSmoothingEnabled = false; // NEXT CODE WILL BE ADDED HERE}Now it’s time to create a pixelated version of the uploaded image and draw it onto our hidden canvas.

// We'll be pixelating the image by thresholdlet percent = 0;// Set threshold to 9.

8 if it's 10 so the blurred faces aren't rendered whitethreshold === 10?.percent = 1 – (9.

8 / 10): percent = 1 – (threshold / 10);// Calculate the scaled dimensionsconst scaledWidth = image.

width * percent;const scaledHeight = image.

height * percent;// Render image smallerhiddenCtx.

drawImage(newImage, 0, 0, scaledWidth, scaledHeight);// Stretch the smaller image onto larger contexthiddenCtx.

drawImage(hiddenCanvas, 0, 0, scaledWidth, scaledHeight, 0, 0, image.

width, image.

height);// NEXT CODE WILL BE ADDED HEREThe last thing to do is to clear the visible canvas and draw the original image to it then draw the pixelated faces on top of it.

// Clear visible canvas and draw original image to it outputCtx.

clearRect(0,0, image.

width, image.

height); outputCtx.

drawImage(newImage, 0, 0); // Draw pixelated faces to canvas this.

getFaces(data).

forEach(face => outputCtx.

putImageData( hiddenCtx.

getImageData( face.

x, face.

y, face.

w, face.

h ), face.

x, face.

y ) )}We’re almost doneThe last thing to do is to head back into App.

js, import “blurFaces.

js” and add it to the render passing in the state.

import BlurFaces from '.

/blurFaces';In the last column we have in our “App.

js” render (<Col md=“6”>); add the code inside it.

<BlurFaces image={image} threshold={threshold} data={data} smooth={smooth}/>Let’s test it outUpload image:Click “Blur Faces”:Uncheck smooth:You can right-click the blurred/pixelated image to save it or you could make an export function.

Have fun incorporating this into your next project and do great things!If you enjoyed this, check out my face verification tutorial using HTML & JS.. More details

Leave a Reply