React & React Native/라이브러리 활용
[RN] React Native Tensorflow.js 활용하기 : Tensor to Image
adjh54
2023. 8. 18. 23:00
반응형
해당 글에서는 Tensorflow에서 TesorCamera로 출력된 값을 이미지로 출력하는 방법에 대해서 공유합니다.
1) 모델 불러오기
💡 React-native에서 Tensorflow의 backend를 지정하고 모델을 불러옵니다.
1. useEffect 후 initStudyReady 함수를 호출합니다.
2. initStudyReady() 함수에서는 Tensorflow.js를 로드합니다.
import * as tf from "@tensorflow/tfjs";
/**
* 최초 Tensor를 사용하기 위한 모델을 불러옵니다.
*/
useEffect(() => {
initStudyReady()
}, []);
/**
* 모델 로드
*
* @return {Promise<void>}
*/
const initStudyReady = async (): Promise<void> => {
let _tensorReady: boolean = false;
await tf.setBackend('rn-webgl')
.then(() => {
console.log("[+] Load Tensorflow.js....");
_tensorReady = true;
tf.engine().startScope(); // 모든 텐서와 백엔드를 추적하는 엔진 영역을 만듬.
})
.catch((error: string) => { throw new Error(`[-] Tensorflow Ready Error: ${error}`); });
setIsReadyTensor(_tensorReady)
}
2) TensorCamera 출력
💡 모델이 불러와지면 TensorCamera를 화면상에 출력합니다.
1. useEffect 후 initStudyReady 함수를 호출합니다.
2. initStudyReady() 함수에서는 Tensorflow.js를 로드합니다.
3. TensorCamera를 출력합니다.
4. TensorCamera에서 출력되는 TensorImage를 반환받습니다.
import React, { createRef, RefObject, useEffect, useRef, useState } from "react";
import { Alert, Image, Modal, Pressable, SafeAreaView, StyleSheet, Text, TouchableHighlight, TouchableOpacity, View } from "react-native";
import { Camera, CameraType } from 'expo-camera';
import * as tf from "@tensorflow/tfjs";
import { InferenceSession, Tensor } from "onnxruntime-react-native";
import { cameraWithTensors } from '@tensorflow/tfjs-react-native';
import { CommonType } from "types/common/CommonType";
import { DeviceType } from "types/common/DeviceType";
import { FaceDetetorType } from "types/FaceDetectorType";
import DeviceInfoUtil from "common/utils/DeviceInfoUtil";
import styles from "./styles/StudyScreenStyle"
const TensorCamera = cameraWithTensors(Camera)
import { ScrollView } from "react-native-gesture-handler";
const TensorScreen = ({ route, navigation, appState }: CommonType.CommonProps) => {
const tensorCameraRef: RefObject<any> = createRef<Camera>(); // TensorCamera 속성 관리
const TEXTURE_DIMS: DeviceType.TextureDims = DeviceInfoUtil.getTextureDims() // TensorCamera의 미리보기 운영체제 별 너비/높이 지정
const [isReadyTensor, setIsReadyTensor] = useState<boolean>(false);
/**
* 최초 Tensor를 사용하기 위한 모델을 불러옵니다.
*/
useEffect(() => {
initStudyReady()
}, []);
/**
* 모델 로드
*
* @return {Promise<void>}
*/
const initStudyReady = async (): Promise<void> => {
let _tensorReady: boolean = false;
await tf.setBackend('rn-webgl')
.then(() => {
console.log("[+] Load Tensorflow.js....");
_tensorReady = true;
tf.engine().startScope(); // 모든 텐서와 백엔드를 추적하는 엔진 영역을 만듬.
})
.catch((error: string) => { throw new Error(`[-] Tensorflow Ready Error: ${error}`); });
setIsReadyTensor(_tensorReady)
}
/**
* TensorCamera가 렌더링 된 이후에 수행되며 TensorImage를 반환해줍니다.
*
* @param {IterableIterator<tf.Tensor3D>} images 텐서 카메라에서 전달 받은 이미지
* @param {void} updatePreview
* @param {WebGLRenderingContext} gl
* @return {Promise<void>}
*/
const readyTensorCamera = async (images: IterableIterator<tf.Tensor3D>, updatePreview: void, gl: WebGLRenderingContext): Promise<void> => {
// [STEP2] TensorCamera를 수행하면서 반복적으로 루프를 수행합니다.
const _loop = async () => {
console.log(`1. 루프 시작 시점에 메모리에 있는 텐서 수 ${tf.memory().numTensors}`)
const _imageToTensor: tf.Tensor3D = images.next().value;
if (_imageToTensor == null) return
else {
}
}
_loop();
}
// ==============================================================================================================================
return (
<SafeAreaView>
<ScrollView style={styles.container}>
<View>
{
isReadyTensor ?
(
<TensorCamera
ref={tensorCameraRef} // 속성 정보
style={styles.camera} // 스타일
type={CameraType.front} // 카메라의 앞, 뒤 방향
cameraTextureHeight={TEXTURE_DIMS.height} // 카메라 미리 보기 높이 값
cameraTextureWidth={TEXTURE_DIMS.width} // 카메라 미리 보기 너비 값
resizeHeight={292} // 출력 카메라 높이
resizeWidth={320} // 출력 카메라 너비
resizeDepth={3} // 출력 텐서의 깊이(채널 수)값. (3 or 4)
autorender={true} // 뷰가 카메라 내용으로 자동업데이트 되는지 여부. (렌더링 발생시 직접적 제어를 원하면 false 값으로 설정할 것)
useCustomShadersToResize={false} // 커스텀 셰이더를 사용하여 출력 텐서에 맞는 더 작은 치수로 카메라 이미지의 크기를 조정할지 여부.
onReady={(images, updatePreview, gl) => readyTensorCamera(images, updatePreview(), gl)} // 컴포넌트가 마운트되고 준비되면 이 콜백이 호출되고 다음 3가지 요소를 받습니다.
/>
)
:
// [CASE2] 모델들이 로드 되지 않았을 경우 처리
<View><Text>TensorCamera를 불러오는 중입니다..</Text></View>
}
</View>
</ScrollView>
</SafeAreaView >
)
}
export default TensorScreen;
3) TensorImage를 이미지의 base64로 변환
💡 TensorCamea에서 출력한 값을 전달 받아서 base64형태의 문자열로 변형을 하는 함수입니다.
1. 전달 받은 TensorImage를 좌우 반전합니다.
2. TensorImage에서 높이와 너비 값을 반환 받습니다.
3. 하나의 차원을 만듭니다.
4. tensorImage는 3차원이며 이전에 만든 차원을 합쳐서 4차원으로 만듭니다.
5. 구성한 버퍼에서 정보를 추출합니다.
6. 정제된 Tensor 값을 통해 jpeg로 인코딩합니다.jpeg 데이터를 'base64'로 전환합니다.
import * as tf from "@tensorflow/tfjs";
import * as jpeg from "jpeg-js";
/**
* TensorCamera에 출력된 TensorImage 값을 기반으로 이미지 출력을 위한 이미지 경로를 반환합니다.
* @param {tf.Tensor3D} tensorImage
* @return {string} 이미지 경로 반환
*/
cvtTensorImgToBase64 = (tensorImage: tf.Tensor3D): string => {
// [STEP1] 전달 받은 TensorImage를 좌우 반전합니다.
const flippedTensor = tf.reverse(tensorImage, [1]);
// [STEP2] TensorImage에서 높이와 너비 값을 반환 받습니다.
const height: number = flippedTensor.shape[0];
const width: number = flippedTensor.shape[1];
// [STEP3] 하나의 차원을 만듭니다.
const tensor3D = tf.fill([height, width, 1], 255);
// [STEP4] tensorImage는 3차원이며 이전에 만든 차원을 합쳐서 4차원으로 만듭니다.
const data = new Buffer(
tf.slice(
tf.concat([flippedTensor, tensor3D], -1), [0], [height, width, 4]).dataSync()
)
// [STEP5] 구성한 버퍼에서 정보를 추출합니다.
const rawImageData = { data, width, height };
// [기능-1] 정제된 Tensor 값을 통해 jpeg로 인코딩한다.
const jpegImageData = jpeg.encode(rawImageData, 100);
// [기능-2] jpeg 데이터를 'base64'로 전환한다.
const imgBase64 = tf.util.decodeString(jpegImageData.data, "base64");
return `data:image/jpeg;base64,${imgBase64}`
}
4) 반환 받은 base64를 이미지로 출력
💡 TensorCamera에서 결과값으로 받는 TensorImage 값이 존재하는 경우 함수를 수행시켜서 base64 문자열로 반환 받습니다.
💡 반환 받은 값은 State내에 넣어두고 <Image> 태그 내에 출력을 합니다
const [images, setImages] = useState<string>("");
/**
* TensorCamera가 렌더링 된 이후에 수행되며 TensorImage를 반환해줍니다.
*
* @param {IterableIterator<tf.Tensor3D>} images 텐서 카메라에서 전달 받은 이미지
* @param {void} updatePreview
* @param {WebGLRenderingContext} gl
* @return {Promise<void>}
*/
const readyTensorCamera = async (images: IterableIterator<tf.Tensor3D>, updatePreview: void, gl: WebGLRenderingContext): Promise<void> => {
// [STEP1] 전체 루프 개수와 '60개 별' 루프 개수를 구성합니다.
let _accTotalLoopCnt = 0; // 루프의 총 개수
let _accLoopCnt = 0; // 60회에 따른 루프 개수
// [STEP2] TensorCamera를 수행하면서 반복적으로 루프를 수행합니다.
const _loop = async () => {
console.log(`1. 루프 시작 시점에 메모리에 있는 텐서 수 ${tf.memory().numTensors}`)
const _imageToTensor: tf.Tensor3D = images.next().value;
if (_imageToTensor == null) return
else {
setImages(CalcStudyModule.cvtTensorImgToBase64(_imageToTensor));
}
}
_loop();
}
return(
<View>
<Image source={images !== "" ? { uri: images } : { uri: "" }} resizeMode="cover" />
</View>
)
5) 기타 참고 사이트
1. Tensorflow의 Blazeface 모델 활용 방법
💡 [참고] Tensorflow의 blazeface를 모델을 이용한 얼굴 측정에 대해 관심 있으시면 아래의 글을 참고하시면 도움이 됩니다.
2. Tensorflow의 메모리 관리 방법
💡 [참고] Tensorflow의 메모리를 관리하는 방법에 관심 있으시면 아래의 글을 참고하시면 도움이 됩니다.
3. Tensorflow와 ONNX 활용 방법
반응형