September 01, 2019
해당 포스트는 Nomad Coder의 초보를 위한 RN강의를 정리한 내용입니다.
state로 관리지난 포스트에는 카메라 타입(전방/후방)을 변경하는 것까지 진행했습니다.
이번 포스트에서는 얼굴을 인식하고 웃는 얼굴일 경우 사진을 찍도록 해보겠습니다.
Expo 공식 문서를 확인해 보면 onFacesDetected속성이 존재한다.
일단 아래와 같이 함수를 구현하고 해당 속성에 전달해 테스트 해보자.
<Camera
style={{
width: width - 40,
height: height / 1.5,
borderRadius: 10,
overflow: "hidden"
}}
type={cameraType}
onFacesDetected={this.onFacesDetected}
/>
...
onFacesDetected = faces => {
console.log(faces);
};아래와 같이 얼굴을 잘 찾는 것을 확인할 수 있다.

얼굴을 찾지 못하면 아래와 같이 빈 array가 반환된다.
expo의 FaceDetector모듈을 추가하고
Camera 컴포넌트에 faceDetectionClassifications속성을 all로 지정해준다.
다음으로 faceDetectorSettings속성을 작성한다.
import * as FaceDetector from 'expo-face-detector';
...
<Camera
style={{
width: width - 40,
height: height / 1.5,
borderRadius: 10,
overflow: "hidden"
}}
type={cameraType}
onFacesDetected={this.onFacesDetected}
faceDetectionClassifications="all"
faceDetectorSettings={{
detectLandmarks: FaceDetector.Constants.Landmarks.all
}}
/>얼굴의 모든 부분의 좌표를 찾는 것을 확인할 수 있다.

faceDetectorSettings에 runClassifications를 추가한다.
<Camera
...
faceDetectionClassifications="all"
faceDetectorSettings={{
detectLandmarks:
FaceDetector.Constants.Landmarks.all,
runClassifications:
FaceDetector.Constants.Classifications.all
}}
/>이전과 같은 이미지를 가지고 테스트하여 콘솔을 확인한 결과
smilingProbability라는 웃는 얼굴일 가능성을 확인할 수 있는 값이 생겼다.
아래와 같이 smileDetected라는 state를 추가해준다.
export default class App extends React.Component {
state = {
hasPermission: null,
cameraType: Camera.Constants.Type.front,
smileDetected: false
};
...
render() {
const { hasPermission, cameraType, smileDetected } = this.state;
...Camera 컴포넌트의 onFacesDetected를 아래와 같이 수정한다
<Camera
style={{
width: width - 40,
height: height / 1.5,
borderRadius: 10,
overflow: 'hidden',
}}
type={cameraType}
onFacesDetected={smileDetected ? null : this.onFacesDetected}
faceDetectionClassifications="all"
FaceDetectorSettings={{
detectLandmarks: FaceDetector.Constants.Landmarks.all,
runClassifications: FaceDetector.Constants.Classifications.all,
}}
/>smileDetected가 true가 되면 onFaceDetected에 null이 들어가 중지된다.
onFacesDetected를 수정해 사용한다.
faces객체 내부의 smilingProbability에 따라 state를 변경하도록 한다.
아래와 같이 JSON을 Destructuring해서 필요한 부분만 가져온다.
onFacesDetected = faces => {
const {
faces: [face],
} = faces
if (face) {
console.log(face)
}
}출력 결과는 아래와 같다.
Object {
"bottomMouthPosition": Object {
"x": 356.0579520321626,
"y": 205.984289517859,
},
...
"rightMouthPosition": Object {
"x": 361.66983528473065,
"y": 201.3538326735288,
},
...
"smilingProbability": 0.029783526435494423,
"yawAngle": 34.714847564697266,
}Object에서 필요한 smilingProbability를 사용해 함수를 작성한다.
onFacesDetected = faces => {
const {
faces: [face],
} = faces
if (face) {
console.log(face.smilingProbability)
if (face.smilingProbability > 0.7) {
this.setState({
smileDetected: true,
})
}
}
}smilingProbability가 낮은 사진에서는 얼굴을 계속 찾고있고
smilingProbability가 높은 사진에서는 smileDetected가 true가 되었고
얼굴을 더 이상 감지하지 않음을 확인할 수 있다.

constructor를 사용해 아래와 같이 코드를 작성한다.
React의 createRef를 이용해 Reference를 만들고 Camera에 전달한다.
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
hasPermission: null,
cameraType: Camera.Constants.Type.front,
smileDetected: false
};
this.cameraRef = React.createRef();
}
...
render() {
const { hasPermission, cameraType, smileDetected } = this.state;
if (hasPermission === true) {
return (
<CenterView>
<Camera
...
ref={this.cameraRef}
/>
...Camera에 전달된 ref가 존재하는지 확인한다.takePictureAsync함수를 사용해 사진을 찍는다.uri를 가져와 확인한다.uri가 존재할 경우 savePhoto함수를 사용해 저장한다.state 변경takePhoto = async () => {
try {
if (this.cameraRef.current) {
let { uri } = await this.cameraRef.current.takePictureAsync({
quality: 1,
})
if (uri) {
this.savePhoto(uri)
}
}
} catch (error) {
alert(error)
this.setState({
smileDetected: false,
})
}
}찍은 사진의 uri는 저장하지 않으면 사라지기때문에
사진을 저장하는 함수를 작성해 따로 저장해줘야한다.
expo-media-library모듈 추가 및 앨범 이름을 선언한다.
import * as MediaLibrary from "expo-media-library";
...
const ALBUM_NAME = "Smiley Cam";아래와 같이 savePhoto함수를 작성한다.
askAsync를 사용해 카메라 권한이 있는지 확인한다.createAssetAsync를 사용해 asset생성한다.getAlbumAsync를 사용해 선언한 앨범의 이름을 가져온다.createAlbumAsync를 사용해 생성 및 사진 저장addAssetsToAlbumAsync를 사용해 사진 저장smileDetected를 false로 변경해 다시 사진 촬영 가능hasPermission을 false로 변경savePhoto = async uri => {
try {
const { status } = await Permissions.askAsync(Permissions.CAMERA_ROLL)
if (status === 'granted') {
const asset = await MediaLibrary.createAssetAsync(uri)
let album = await MediaLibrary.getAlbumAsync(ALBUM_NAME)
if (album === null) {
album = await MediaLibrary.createAlbumAsync(ALBUM_NAME, asset)
} else {
await MediaLibrary.addAssetsToAlbumAsync([asset], album.id)
}
setTimeout(
() =>
this.setState({
smileDetected: false,
}),
2000
)
} else {
this.setState({ hasPermission: false })
}
} catch (error) {
console.log(error)
}
}smilingProbability가 0.7보다 커진 경우 사진이 찍혔으며
Smiley Cam폴더에 사진이 저장된 것을 확인할 수 있다.
지금까지 4개의 포스트에 거쳐 React Native에서 카메라를 사용해
웃는 얼굴이 감지된 경우 사진이 찍히는 어플리케이션을 제작해보았다.
자세한 코드는 여기에서 확인할 수 있습니다.