add caching model
This commit is contained in:
parent
957a63ca7c
commit
c7f37f589e
|
@ -2,12 +2,14 @@ import LayoutPage from "@/components/templates/LayoutPage";
|
||||||
import { useEffect, useRef, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import { FaCircleCheck } from "react-icons/fa6";
|
import { FaCircleCheck } from "react-icons/fa6";
|
||||||
import * as tf from "@tensorflow/tfjs";
|
import * as tf from "@tensorflow/tfjs";
|
||||||
import { FilesetResolver, HandLandmarker } from "@mediapipe/tasks-vision";
|
import { HandLandmarker } from "@mediapipe/tasks-vision";
|
||||||
import calcLandmarkList from "@/utils/CalculateLandmark";
|
import calcLandmarkList from "@/utils/CalculateLandmark";
|
||||||
import preProcessLandmark from "@/utils/PreProcessLandmark";
|
import preProcessLandmark from "@/utils/PreProcessLandmark";
|
||||||
import ConvertResult from "@/utils/ConvertResult";
|
import ConvertResult from "@/utils/ConvertResult";
|
||||||
import useNavbarStore from "@/stores/NavbarStore";
|
import useNavbarStore from "@/stores/NavbarStore";
|
||||||
import { AnimatePresence, motion } from "framer-motion";
|
import { AnimatePresence, motion } from "framer-motion";
|
||||||
|
import { loadTensorFlowModel } from "@/utils/tensorflowModelLoader";
|
||||||
|
import { loadHandLandmarker } from "@/utils/handLandmarkerLoader";
|
||||||
|
|
||||||
type PredictResult = {
|
type PredictResult = {
|
||||||
abjad: String;
|
abjad: String;
|
||||||
|
@ -50,12 +52,13 @@ const Home = () => {
|
||||||
const loadModel = async () => {
|
const loadModel = async () => {
|
||||||
setLoadCamera(false);
|
setLoadCamera(false);
|
||||||
try {
|
try {
|
||||||
const lm = await tf.loadLayersModel("/model/model.json");
|
const lm = await loadTensorFlowModel();
|
||||||
model = lm;
|
model = lm;
|
||||||
|
|
||||||
const emptyInput = tf.tensor2d([[0, 0]]);
|
// const emptyInput = tf.tensor2d([[0, 0]]);
|
||||||
|
|
||||||
model.predict(emptyInput) as tf.Tensor;
|
// model.predict(emptyInput) as tf.Tensor;
|
||||||
|
|
||||||
|
|
||||||
setLoadCamera(true);
|
setLoadCamera(true);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -65,17 +68,8 @@ const Home = () => {
|
||||||
|
|
||||||
const initializeHandDetection = async () => {
|
const initializeHandDetection = async () => {
|
||||||
try {
|
try {
|
||||||
const vision = await FilesetResolver.forVisionTasks(
|
handLandmarker = await loadHandLandmarker();
|
||||||
"https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@latest/wasm"
|
|
||||||
);
|
|
||||||
handLandmarker = await HandLandmarker.createFromOptions(vision, {
|
|
||||||
baseOptions: {
|
|
||||||
modelAssetPath: `https://storage.googleapis.com/mediapipe-models/hand_landmarker/hand_landmarker/float16/1/hand_landmarker.task`,
|
|
||||||
},
|
|
||||||
numHands: 2,
|
|
||||||
runningMode: "VIDEO",
|
|
||||||
});
|
|
||||||
|
|
||||||
detectHands();
|
detectHands();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error initializing hand detection:", error);
|
console.error("Error initializing hand detection:", error);
|
||||||
|
@ -117,11 +111,13 @@ const Home = () => {
|
||||||
performance.now()
|
performance.now()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
setHandPresence(detections.handedness.length > 0);
|
setHandPresence(detections.handedness.length > 0);
|
||||||
// Assuming detections.landmarks is an array of landmark objects
|
// Assuming detections.landmarks is an array of landmark objects
|
||||||
if (detections.landmarks) {
|
if (detections.landmarks) {
|
||||||
if (detections.handednesses.length > 0) {
|
if (detections.handednesses.length > 0) {
|
||||||
console.log(detections);
|
// console.log(detections);
|
||||||
|
|
||||||
if (detections.handednesses[0][0].displayName === "Right") {
|
if (detections.handednesses[0][0].displayName === "Right") {
|
||||||
const landm = detections.landmarks[0].map((landmark) => landmark);
|
const landm = detections.landmarks[0].map((landmark) => landmark);
|
||||||
|
@ -184,10 +180,7 @@ const Home = () => {
|
||||||
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||||
></path>
|
></path>
|
||||||
</svg>
|
</svg>
|
||||||
<p className="ml-2">
|
<p className="ml-2">Gunakan tangan kanan. Pencahayaan ideal 300 Lux, jarak maksimal 2 meter dari kamera.</p>
|
||||||
Gunakan tangan kanan dan pastikan gambar pada kamera terlihat
|
|
||||||
jelas
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={() => setInfo(false)}
|
onClick={() => setInfo(false)}
|
||||||
|
|
|
@ -80,6 +80,12 @@ const Kamus = () => {
|
||||||
<div className="flex flex-col md:flex-row gap-6 overflow-y-auto flex-1">
|
<div className="flex flex-col md:flex-row gap-6 overflow-y-auto flex-1">
|
||||||
<div className="flex flex-col order-2 md:order-1">
|
<div className="flex flex-col order-2 md:order-1">
|
||||||
<p>{selectedKamus?.keterangan}</p>
|
<p>{selectedKamus?.keterangan}</p>
|
||||||
|
|
||||||
|
<a href="/">
|
||||||
|
<button className="btn mt-8 bg-slate-900 text-white hover:bg-slate-950 w-fit">
|
||||||
|
Coba Sekarang
|
||||||
|
</button>
|
||||||
|
</a>
|
||||||
{/* <div className="flex flex-wrap gap-2 mt-3">
|
{/* <div className="flex flex-wrap gap-2 mt-3">
|
||||||
{selectedKamus?.badge.map((item, index) => (
|
{selectedKamus?.badge.map((item, index) => (
|
||||||
<span
|
<span
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import LayoutPage from "@/components/templates/LayoutPage";
|
import LayoutPage from "@/components/templates/LayoutPage";
|
||||||
import { useEffect, useRef, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import * as tf from "@tensorflow/tfjs";
|
import * as tf from "@tensorflow/tfjs";
|
||||||
import { FilesetResolver, HandLandmarker } from "@mediapipe/tasks-vision";
|
import { HandLandmarker } from "@mediapipe/tasks-vision";
|
||||||
import calcLandmarkList from "@/utils/CalculateLandmark";
|
import calcLandmarkList from "@/utils/CalculateLandmark";
|
||||||
import preProcessLandmark from "@/utils/PreProcessLandmark";
|
import preProcessLandmark from "@/utils/PreProcessLandmark";
|
||||||
import { abjads } from "@/utils/ConvertResult";
|
import { abjads } from "@/utils/ConvertResult";
|
||||||
|
@ -10,6 +10,8 @@ import { MdOutlineQuiz } from "react-icons/md";
|
||||||
import useMenyusunHurufStore from "@/stores/MenyusunHurufStore";
|
import useMenyusunHurufStore from "@/stores/MenyusunHurufStore";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import Swal from "sweetalert2";
|
import Swal from "sweetalert2";
|
||||||
|
import { loadTensorFlowModel } from "@/utils/tensorflowModelLoader";
|
||||||
|
import { loadHandLandmarker } from "@/utils/handLandmarkerLoader";
|
||||||
|
|
||||||
// type PredictResult = {
|
// type PredictResult = {
|
||||||
// abjad: String;
|
// abjad: String;
|
||||||
|
@ -61,7 +63,7 @@ const Quiz = () => {
|
||||||
const loadModel = async () => {
|
const loadModel = async () => {
|
||||||
setLoadCamera(false);
|
setLoadCamera(false);
|
||||||
try {
|
try {
|
||||||
const lm = await tf.loadLayersModel("/model/model.json");
|
const lm = await loadTensorFlowModel();
|
||||||
model = lm;
|
model = lm;
|
||||||
|
|
||||||
const emptyInput = tf.tensor2d([[0, 0]]);
|
const emptyInput = tf.tensor2d([[0, 0]]);
|
||||||
|
@ -76,16 +78,7 @@ const Quiz = () => {
|
||||||
|
|
||||||
const initializeHandDetection = async () => {
|
const initializeHandDetection = async () => {
|
||||||
try {
|
try {
|
||||||
const vision = await FilesetResolver.forVisionTasks(
|
handLandmarker = await loadHandLandmarker();
|
||||||
"https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@latest/wasm"
|
|
||||||
);
|
|
||||||
handLandmarker = await HandLandmarker.createFromOptions(vision, {
|
|
||||||
baseOptions: {
|
|
||||||
modelAssetPath: `https://storage.googleapis.com/mediapipe-models/hand_landmarker/hand_landmarker/float16/1/hand_landmarker.task`,
|
|
||||||
},
|
|
||||||
numHands: 2,
|
|
||||||
runningMode: "VIDEO",
|
|
||||||
});
|
|
||||||
|
|
||||||
detectHands();
|
detectHands();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import LayoutPage from "@/components/templates/LayoutPage";
|
import LayoutPage from "@/components/templates/LayoutPage";
|
||||||
import { useEffect, useRef, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import * as tf from "@tensorflow/tfjs";
|
import * as tf from "@tensorflow/tfjs";
|
||||||
import { FilesetResolver, HandLandmarker } from "@mediapipe/tasks-vision";
|
import { HandLandmarker } from "@mediapipe/tasks-vision";
|
||||||
import calcLandmarkList from "@/utils/CalculateLandmark";
|
import calcLandmarkList from "@/utils/CalculateLandmark";
|
||||||
import preProcessLandmark from "@/utils/PreProcessLandmark";
|
import preProcessLandmark from "@/utils/PreProcessLandmark";
|
||||||
import ConvertResult, { abjads } from "@/utils/ConvertResult";
|
import ConvertResult, { abjads } from "@/utils/ConvertResult";
|
||||||
|
@ -11,6 +11,8 @@ import { MdOutlineQuiz } from "react-icons/md";
|
||||||
import useTebakHurufStore from "@/stores/TebakHurufStore";
|
import useTebakHurufStore from "@/stores/TebakHurufStore";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import Swal from "sweetalert2";
|
import Swal from "sweetalert2";
|
||||||
|
import { loadTensorFlowModel } from "@/utils/tensorflowModelLoader";
|
||||||
|
import { loadHandLandmarker } from "@/utils/handLandmarkerLoader";
|
||||||
|
|
||||||
// type PredictResult = {
|
// type PredictResult = {
|
||||||
// abjad: String;
|
// abjad: String;
|
||||||
|
@ -63,7 +65,7 @@ const Quiz = () => {
|
||||||
const loadModel = async () => {
|
const loadModel = async () => {
|
||||||
setLoadCamera(false);
|
setLoadCamera(false);
|
||||||
try {
|
try {
|
||||||
const lm = await tf.loadLayersModel("/model/model.json");
|
const lm = await loadTensorFlowModel();
|
||||||
model = lm;
|
model = lm;
|
||||||
|
|
||||||
const emptyInput = tf.tensor2d([[0, 0]]);
|
const emptyInput = tf.tensor2d([[0, 0]]);
|
||||||
|
@ -78,16 +80,7 @@ const Quiz = () => {
|
||||||
|
|
||||||
const initializeHandDetection = async () => {
|
const initializeHandDetection = async () => {
|
||||||
try {
|
try {
|
||||||
const vision = await FilesetResolver.forVisionTasks(
|
handLandmarker = await loadHandLandmarker();
|
||||||
"https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@latest/wasm"
|
|
||||||
);
|
|
||||||
handLandmarker = await HandLandmarker.createFromOptions(vision, {
|
|
||||||
baseOptions: {
|
|
||||||
modelAssetPath: `https://storage.googleapis.com/mediapipe-models/hand_landmarker/hand_landmarker/float16/1/hand_landmarker.task`,
|
|
||||||
},
|
|
||||||
numHands: 2,
|
|
||||||
runningMode: "VIDEO",
|
|
||||||
});
|
|
||||||
|
|
||||||
detectHands();
|
detectHands();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -161,11 +154,13 @@ const Quiz = () => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
setShowAnswer(false);
|
setShowAnswer(false);
|
||||||
|
|
||||||
isLoading = false;
|
setTimeout(() => {
|
||||||
setProgress(0);
|
isLoading = false;
|
||||||
noSoal++;
|
setProgress(0);
|
||||||
previousResult = [];
|
noSoal++;
|
||||||
quizStore.setSoalIndex(noSoal);
|
previousResult = [];
|
||||||
|
quizStore.setSoalIndex(noSoal);
|
||||||
|
}, 500);
|
||||||
|
|
||||||
if (noSoal === quizStore.listSoal.length) {
|
if (noSoal === quizStore.listSoal.length) {
|
||||||
quizStore.setSession(false);
|
quizStore.setSession(false);
|
||||||
|
@ -231,7 +226,7 @@ const Quiz = () => {
|
||||||
const store = useNavbarStore();
|
const store = useNavbarStore();
|
||||||
const quizStore = useTebakHurufStore();
|
const quizStore = useTebakHurufStore();
|
||||||
|
|
||||||
console.log(quizStore.jawaban);
|
// console.log(quizStore.jawaban);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!quizStore.session) {
|
if (!quizStore.session) {
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
import { FilesetResolver, HandLandmarker } from "@mediapipe/tasks-vision";
|
||||||
|
import { getHandLandmarkerModel, saveHandLandmarkerModel } from "./indexedDBHelper";
|
||||||
|
|
||||||
|
let handLandmarker: HandLandmarker | null = null;
|
||||||
|
|
||||||
|
export async function loadHandLandmarker(): Promise<HandLandmarker> {
|
||||||
|
if (handLandmarker) return handLandmarker; // Jika model sudah ada, langsung kembalikan
|
||||||
|
|
||||||
|
let modelBlob = await getHandLandmarkerModel();
|
||||||
|
|
||||||
|
if (!modelBlob) {
|
||||||
|
console.log("🔄 Model Hand Landmarker tidak ditemukan di cache, mengunduh...");
|
||||||
|
const response = await fetch(
|
||||||
|
"https://storage.googleapis.com/mediapipe-models/hand_landmarker/hand_landmarker/float16/1/hand_landmarker.task"
|
||||||
|
);
|
||||||
|
modelBlob = await response.blob();
|
||||||
|
await saveHandLandmarkerModel(modelBlob);
|
||||||
|
} else {
|
||||||
|
console.log("✅ Model Hand Landmarker ditemukan di cache, menggunakan model lokal.");
|
||||||
|
}
|
||||||
|
|
||||||
|
const vision = await FilesetResolver.forVisionTasks(
|
||||||
|
"https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@latest/wasm"
|
||||||
|
);
|
||||||
|
|
||||||
|
const modelURL = URL.createObjectURL(modelBlob);
|
||||||
|
handLandmarker = await HandLandmarker.createFromOptions(vision, {
|
||||||
|
baseOptions: { modelAssetPath: modelURL },
|
||||||
|
numHands: 2,
|
||||||
|
runningMode: "VIDEO",
|
||||||
|
});
|
||||||
|
|
||||||
|
return handLandmarker;
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
import * as tf from "@tensorflow/tfjs";
|
||||||
|
|
||||||
|
const DB_NAME = "ModelCacheDB";
|
||||||
|
const STORE_NAME = "models";
|
||||||
|
const TENSORFLOW_MODEL_KEY = "tensorflow_model";
|
||||||
|
const HAND_LANDMARKER_MODEL_KEY = "hand_landmarker_model";
|
||||||
|
|
||||||
|
// Membuka IndexedDB
|
||||||
|
export function openDB(): Promise<IDBDatabase> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const request = indexedDB.open(DB_NAME, 1);
|
||||||
|
|
||||||
|
request.onupgradeneeded = (event: IDBVersionChangeEvent) => {
|
||||||
|
const db = (event.target as IDBOpenDBRequest).result;
|
||||||
|
if (!db.objectStoreNames.contains(STORE_NAME)) {
|
||||||
|
db.createObjectStore(STORE_NAME);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
request.onsuccess = () => resolve(request.result);
|
||||||
|
request.onerror = () => reject(request.error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 🟢 **Simpan Model TensorFlow.js ke IndexedDB**
|
||||||
|
export async function saveTensorFlowModel(model: tf.LayersModel): Promise<void> {
|
||||||
|
await model.save(`indexeddb://${TENSORFLOW_MODEL_KEY}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 🟢 **Ambil Model TensorFlow.js dari IndexedDB**
|
||||||
|
export async function getTensorFlowModel(): Promise<tf.LayersModel | null> {
|
||||||
|
try {
|
||||||
|
return await tf.loadLayersModel(`indexeddb://${TENSORFLOW_MODEL_KEY}`);
|
||||||
|
} catch (error) {
|
||||||
|
console.warn("⚠️ Model TensorFlow tidak ditemukan di cache:", error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 🟢 **Simpan Model Hand Landmarker ke IndexedDB**
|
||||||
|
export async function saveHandLandmarkerModel(blob: Blob): Promise<void> {
|
||||||
|
const db = await openDB();
|
||||||
|
const transaction = db.transaction(STORE_NAME, "readwrite");
|
||||||
|
const store = transaction.objectStore(STORE_NAME);
|
||||||
|
store.put(blob, HAND_LANDMARKER_MODEL_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 🟢 **Ambil Model Hand Landmarker dari IndexedDB**
|
||||||
|
export async function getHandLandmarkerModel(): Promise<Blob | null> {
|
||||||
|
const db = await openDB();
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const transaction = db.transaction(STORE_NAME, "readonly");
|
||||||
|
const store = transaction.objectStore(STORE_NAME);
|
||||||
|
const request = store.get(HAND_LANDMARKER_MODEL_KEY);
|
||||||
|
|
||||||
|
request.onsuccess = () => resolve(request.result as Blob | null);
|
||||||
|
request.onerror = () => reject(request.error);
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
import * as tf from "@tensorflow/tfjs";
|
||||||
|
import { getTensorFlowModel, saveTensorFlowModel } from "./indexedDBHelper";
|
||||||
|
|
||||||
|
let model: tf.LayersModel | null = null;
|
||||||
|
|
||||||
|
export async function loadTensorFlowModel(): Promise<tf.LayersModel> {
|
||||||
|
if (model) return model; // Jika model sudah dimuat, langsung kembalikan
|
||||||
|
|
||||||
|
model = await getTensorFlowModel();
|
||||||
|
|
||||||
|
if (!model) {
|
||||||
|
console.log("🔄 Model TensorFlow tidak ditemukan di cache, mengunduh...");
|
||||||
|
model = await tf.loadLayersModel("/model/model.json");
|
||||||
|
await saveTensorFlowModel(model);
|
||||||
|
} else {
|
||||||
|
console.log("✅ Model TensorFlow ditemukan di cache, menggunakan model lokal.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return model;
|
||||||
|
}
|
Loading…
Reference in New Issue