1) 클라우드에서 캔버스로 이미지 생성
2) 이미지를 서버로 보냄
3) 서버에서 google vision api로 이미지 텍스트 인식
4) 텍스트를 클라우드로 보냄
-백엔드-
config - <vision.js> : 이미지 텍스트 인식 기능
const vision = require("@google-cloud/vision");
const CREDENTIALS = {'사용자 계정 비공개 키'};
const CONFIG = {
credentials: {
private_key: CREDENTIALS.private_key,
client_email: CREDENTIALS.client_email,
},
};
const client = new vision.ImageAnnotatorClient(CONFIG);
const detectText = async (file_path) => { //이미지 텍스트 함수
try {
const [result] = await client.textDetection(file_path);
if (result.fullTextAnnotation.text) {
return result.fullTextAnnotation.text;
} else {
console.error("No text found in the image.");
return "";
}
} catch (error) {
console.error("Error detecting text:", error);
}
};
module.exports = detectText;
controller - <canvasController.js> : 캔버스 컨트롤러
-base64 : 이진 데이터를 문자 코드에 영향을 받지 않는 공통 아스키 문자로 표현하기 위해 만들어진 인코딩
-인코딩 : 문자나 기호들을 컴퓨터가 이용할 수 있는 기호로 암호화
-Buffer : 이진 데이터들의 스트림을 읽거나, 조작하는 메커니즘.
-from : 지정된 문자열, 배열 또는 버퍼로 채워진 새 버퍼를 만듬
const asynchHandler = require("express-async-handler");
const fs = require("fs");
const detectText = require("../config/vision");
const postCanvas = asynchHandler(async (req, res) => {
const { dataURL } = req.body; //클라이언트에서 이미지 데이터를 받음
const base64Data = dataURL.replace(/^data:image\/\w+;base64,/, "");
//dataURL에서 base64 인코딩된 이미지 데이터를 추출
const buffer = Buffer.from(base64Data, "base64"); //추출된 이미지 데이터를 버퍼로 변환
await new Promise((res, rej) => { //비동기화로 이미지 파일을 생성
fs.writeFile("image.png", buffer, (err) => {
if (err) {
console.error(err);
rej("이미지 저장 중 에러 발생");
} else {
res();
}
});
});
const text = await detectText("image.png"); //이미지 텍스트 인식 실행
res.json({ text }); //텍스트 값을 클라이언트로 보냄
});
module.exports = { postCanvas };
route - <canvasRoute.js> : 캔버스 라우터
const express = require("express");
const router = express.Router();
const { postCanvas } = require("../controller/canvasController");
router.route("/canvas").post(postCanvas);
module.exports = router;
app.js
const express = require("express");
const cors = require("cors");
const dbConnect = require("./config/dbConnect");
const app = express();
dbConnect();
app.use(cors());
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use("/", require("./route/userRoute"));
app.use("/", require("./route/postRoute"));
app.use("/", require("./route/canvasRoute")); //캔버스 라우터
app.listen(5000, () => {
console.log("5000 포트에서 서버 실행 중");
});
-프론트엔드-
page - Canvas - <Canvas.js>
import React, { useEffect, useRef, useState } from "react";
import axios from "axios";
import "../../css/canvas.css";
function Canvas() {
const canvasRef = useRef(null);
const [isDrawing, setIsDrawing] = useState(false);
const [lastX, setLastX] = useState(0);
const [lastY, setLastY] = useState(0);
const [outputImageSrc, setOutputImageSrc] = useState(null);
const [path, setPath] = useState([]);
const [paths, setPaths] = useState([]);
const [imgText, setImgText] = useState(""); //텍스트 값 변수, 함수
useEffect(() => {
const canvas = canvasRef.current;
const ctx = canvas.getContext("2d");
const drawing = (e) => {
if (!isDrawing) return;
const rect = canvas.getBoundingClientRect();
const offsetX = e.clientX - rect.left;
const offsetY = e.clientY - rect.top;
ctx.beginPath();
ctx.moveTo(lastX, lastY);
ctx.lineTo(offsetX, offsetY);
ctx.strokeStyle = "gray";
ctx.lineWidth = 2;
ctx.stroke();
setLastX(offsetX);
setLastY(offsetY);
setPath((prevPath) => [...prevPath, { x: offsetX, y: offsetY }]);
};
canvas.addEventListener("mousemove", drawing);
return () => {
canvas.removeEventListener("mousemove", drawing);
};
}, [isDrawing, lastX, lastY]);
const drawingCanvas = (e) => {
setIsDrawing(true);
const rect = e.target.getBoundingClientRect();
setLastX(e.clientX - rect.left);
setLastY(e.clientY - rect.top);
setPath([{ x: e.clientX - rect.left, y: e.clientY - rect.top }]);
};
const stopDrawing = () => {
if (isDrawing) {
setPaths((prevPaths) => [...prevPaths, path]);
setPath([]);
}
setIsDrawing(false);
};
const canvasOut = () => {
setIsDrawing(false);
};
const outputCanvasImage = async () => {
const canvas = canvasRef.current;
setOutputImageSrc(canvas.toDataURL()); //이미지 데이터 URL 화
const dataURL = canvas.toDataURL("image/png");
await axios
.post("http://localhost:5000/canvas", { dataURL: dataURL }) //이미지 URL을 서버로 보냄
.then((res) => {
setImgText(res.data.text); //서버에서 받은 텍스트 값을 setImgText에 저장
});
};
const clearCanvas = () => {
const canvas = canvasRef.current;
const ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
setOutputImageSrc(null);
setPaths([]);
setImgText("");
};
const returnCurrentLine = () => {
setPaths((prevPaths) => prevPaths.slice(0, -1));
};
const redrawCanvas = () => {
const canvas = canvasRef.current;
const ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
paths.forEach((p) => {
ctx.beginPath();
ctx.moveTo(p[0].x, p[0].y);
for (let i = 1; i < p.length; i++) {
ctx.lineTo(p[i].x, p[i].y);
}
ctx.stroke();
});
};
useEffect(redrawCanvas, [paths]);
return (
<div className="canvas">
<canvas
ref={canvasRef}
width={500}
height={500}
style={{ border: "1px solid black" }}
onMouseDown={drawingCanvas}
onMouseUp={stopDrawing}
onMouseOut={canvasOut}
/>
{outputImageSrc && <img src={outputImageSrc} alt="분석된 이미지" />}
<div>
<button onClick={outputCanvasImage}>추출</button>
<button onClick={clearCanvas}>다시 쓰기</button>
<button onClick={returnCurrentLine}>한 획 지우기</button>
</div>
<div>
<h1>{imgText}</h1> <!--텍스트 값 나타냄-->
</div>
</div>
);
}
export default Canvas;
css - <canvas.css>
.canvas button {
width: 100px;
height: 30px;
font-size: 15px;
font-weight: bold;
border: 1px solid black;
background-color: white;
}
.canvas button:hover {
color: white;
background-color: black;
}
'프로젝트 > 한글 게임' 카테고리의 다른 글
9. 이미지, 낱말 데이터 등록 (0) | 2024.04.17 |
---|---|
8. multer (0) | 2024.04.15 |
6. Google Vision API (0) | 2024.04.14 |
5. 선 한 획 씩 삭제하기 (0) | 2024.04.11 |
4. Canvas 다시 쓰기 (0) | 2024.04.11 |