This commit is contained in:
DuchPanhathun 2024-12-05 14:04:37 +07:00
parent 492ca96937
commit 1a243af925
18 changed files with 138 additions and 91 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,5 +1,5 @@
{
"node": {},
"edge": {},
"encryptionKey": "tSamhz5T2h7ahFqh+YSxMpWNSqXYzyFQavfWbDAznG4="
"encryptionKey": "5bSgyi9JzZrdWPWIOnposJ8ES7uP+I1sTxcA6KjnyC0="
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,36 @@
import React, { useRef, useState } from 'react';
import Webcam from 'react-webcam';
const CameraCapture = ({ onCapture }) => {
const webcamRef = useRef(null);
const [capturedImages, setCapturedImages] = useState([]);
const handleCameraCapture = async () => {
const imageSrc = webcamRef.current.getScreenshot();
if (imageSrc) {
const response = await fetch(imageSrc);
const blob = await response.blob();
const file = new File([blob], `webcam-capture-${Date.now()}.jpg`, { type: 'image/jpeg' });
setCapturedImages(prev => [...prev, file]);
onCapture(file);
}
};
return (
<div className="relative">
<Webcam
ref={webcamRef}
screenshotFormat="image/jpeg"
className="w-full rounded"
/>
<button
onClick={handleCameraCapture}
className="mt-2 px-4 py-2 bg-green-500 text-white rounded hover:bg-green-600 transition-colors w-full"
>
Capture Photo ({capturedImages.length} captured)
</button>
</div>
);
};
export default CameraCapture;

View File

@ -1,38 +1,36 @@
'use client';
import React, { useState, useRef } from 'react';
import React, { useState } from 'react';
import axios from 'axios';
import { saveAs } from 'file-saver';
import Webcam from 'react-webcam';
import CameraCapture from './CameraCapture';
const ImageUploader = () => {
const [selectedFile, setSelectedFile] = useState(null);
const [result, setResult] = useState('');
const [selectedFiles, setSelectedFiles] = useState([]);
const [results, setResults] = useState([]);
const [isLoading, setIsLoading] = useState(false);
const [showCamera, setShowCamera] = useState(false);
const webcamRef = useRef(null);
const [currentProcessingIndex, setCurrentProcessingIndex] = useState(0);
const handleFileChange = (event) => {
if (event.target.files && event.target.files.length > 0) {
setSelectedFile(event.target.files[0]);
handleFileUpload(event.target.files[0]);
const newFiles = Array.from(event.target.files);
setSelectedFiles(prev => [...prev, ...newFiles]);
processFiles(newFiles);
}
};
const handleCameraCapture = async () => {
const imageSrc = webcamRef.current.getScreenshot();
if (imageSrc) {
// Convert base64 to blob
const response = await fetch(imageSrc);
const blob = await response.blob();
const file = new File([blob], "webcam-capture.jpg", { type: 'image/jpeg' });
setSelectedFile(file);
handleFileUpload(file);
const processFiles = async (files) => {
setIsLoading(true);
for (const file of files) {
await handleFileUpload(file);
}
setIsLoading(false);
};
const handleFileUpload = async (file) => {
setIsLoading(true);
const formData = new FormData();
formData.append('image', file);
@ -40,19 +38,18 @@ const ImageUploader = () => {
const response = await axios.post('/api/ocr', formData, {
headers: { 'Content-Type': 'multipart/form-data' },
});
setResult(response.data.text);
setResults(prev => [...prev, { file: file.name, text: response.data.text }]);
} catch (error) {
console.error('Error uploading image:', error);
alert('An error occurred while processing the image');
} finally {
setIsLoading(false);
setResults(prev => [...prev, { file: file.name, text: 'Error processing image' }]);
}
};
const handleDownload = () => {
if (result) {
const blob = new Blob([result], { type: 'application/msword' });
saveAs(blob, 'ocr_result.doc');
if (results.length > 0) {
const combinedText = results.map(r => `File: ${r.file}\n${r.text}\n\n`).join('---\n');
const blob = new Blob([combinedText], { type: 'application/msword' });
saveAs(blob, 'ocr_results.doc');
}
};
@ -61,58 +58,81 @@ const ImageUploader = () => {
};
return (
<div className="max-w-md mx-auto mt-8">
<div className="space-y-4">
<div className="flex space-x-2">
<input
type="file"
accept="image/*"
onChange={handleFileChange}
className="block w-full text-sm text-gray-500
file:mr-4 file:py-2 file:px-4
file:rounded-full file:border-0
file:text-sm file:font-semibold
file:bg-violet-50 file:text-violet-700
hover:file:bg-violet-100"
/>
<button
onClick={toggleCamera}
className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600 transition-colors"
>
{showCamera ? 'Hide Camera' : 'Use Camera'}
</button>
</div>
{showCamera && (
<div className="relative">
<Webcam
ref={webcamRef}
screenshotFormat="image/jpeg"
className="w-full rounded"
/>
<button
onClick={handleCameraCapture}
className="mt-2 px-4 py-2 bg-green-500 text-white rounded hover:bg-green-600 transition-colors w-full"
>
Capture Photo
</button>
</div>
)}
{isLoading && <p className="mt-4">Processing...</p>}
<div className="min-h-screen bg-gray-50 py-12 px-4 sm:px-6 lg:px-8">
<div className="max-w-2xl mx-auto bg-white rounded-lg shadow-lg p-8">
<h2 className="text-2xl font-bold text-gray-900 mb-8 text-center">
Image Text Extractor
</h2>
{result && (
<div className="mt-4">
<h3 className="font-bold">OCR Result:</h3>
<pre className="mt-2 p-2 bg-gray-100 rounded">{result}</pre>
<div className="space-y-6">
<div className="flex flex-col sm:flex-row sm:space-x-4 space-y-4 sm:space-y-0">
<div className="flex-1">
<input
type="file"
accept="image/*"
onChange={handleFileChange}
multiple
className="block w-full text-sm text-gray-500
file:mr-4 file:py-3 file:px-4
file:rounded-lg file:border-0
file:text-sm file:font-semibold
file:bg-violet-100 file:text-violet-700
hover:file:bg-violet-200
cursor-pointer
border border-gray-300 rounded-lg"
/>
</div>
<button
onClick={handleDownload}
className="mt-4 px-4 py-2 bg-green-500 text-white rounded hover:bg-green-600 transition-colors"
onClick={toggleCamera}
className="px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700
transition-colors duration-200 font-medium shadow-sm"
>
Download as .doc
{showCamera ? 'Hide Camera' : 'Use Camera'}
</button>
</div>
)}
{showCamera && (
<div className="border-2 border-dashed border-gray-300 rounded-lg p-4">
<CameraCapture onCapture={(file) => processFiles([file])} />
</div>
)}
{isLoading && (
<div className="text-center py-4">
<div className="animate-spin rounded-full h-10 w-10 border-b-2 border-blue-600 mx-auto"></div>
<p className="mt-2 text-gray-600">Processing images...</p>
</div>
)}
{results.length > 0 && (
<div className="mt-8 space-y-6">
<div className="flex items-center justify-between">
<h3 className="text-xl font-bold text-gray-900">OCR Results</h3>
<button
onClick={handleDownload}
className="inline-flex items-center px-4 py-2 bg-green-600 text-white rounded-lg
hover:bg-green-700 transition-colors duration-200 font-medium shadow-sm"
>
<svg className="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2}
d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" />
</svg>
Download Results
</button>
</div>
<div className="space-y-4">
{results.map((result, index) => (
<div key={index} className="bg-gray-50 rounded-lg p-4 shadow-sm">
<h4 className="font-medium text-gray-900 mb-2">{result.file}</h4>
<pre className="bg-white p-4 rounded-lg border border-gray-200 text-sm text-gray-700
overflow-x-auto">{result.text}</pre>
</div>
))}
</div>
</div>
)}
</div>
</div>
</div>
);