This commit is contained in:
root
2026-07-04 08:01:01 +00:00
parent 73d84169d6
commit 104107bf1d
3 changed files with 96 additions and 34 deletions
@@ -183,6 +183,10 @@ export function Login(username: string, password: string): $CancellablePromise<$
});
}
export function PerformGeminiOcr(base64Image: string): $CancellablePromise<string> {
return $Call.ByID(3593860722, base64Image);
}
/**
* Register creates a new registration application
*/
+58 -34
View File
@@ -209,7 +209,7 @@
class="textarea textarea-ghost focus:bg-base-200/30 w-full min-h-[160px] text-sm sm:text-base leading-relaxed resize-none p-2 mt-2 border-0 focus:border-0 focus:outline-none focus:ring-0"
></textarea>
<div class="flex justify-between items-center mt-2 border-t border-base-200 pt-2">
<div class="flex gap-2">
<div class="flex items-center gap-3">
<button
type="button"
@click="triggerOcrUpload"
@@ -220,6 +220,15 @@
<CameraIcon :size="12" />
종이 답안 인식 (OCR)
</button>
<label class="label cursor-pointer py-0 gap-1.5" title="체크 시 온디바이스 Tesseract 대신 Gemini AI를 이용해 손글씨를 고정밀도로 인식합니다">
<input
type="checkbox"
v-model="useAiForOcr"
:disabled="showFeedback || ocrLoading"
class="checkbox checkbox-xs checkbox-secondary"
/>
<span class="label-text text-[10px] sm:text-xs opacity-70 font-bold">AI 인식 사용</span>
</label>
<input
type="file"
ref="ocrFileInput"
@@ -573,6 +582,7 @@ const ocrProgress = ref(0)
const ocrStatusText = ref('')
const ocrResultText = ref<string | null>(null)
const uploadedOcrImageBase64 = ref('')
const useAiForOcr = ref(false)
function triggerOcrUpload() {
if (ocrFileInput.value) {
@@ -580,6 +590,15 @@ function triggerOcrUpload() {
}
}
function readFileAsDataURL(file: File): Promise<string> {
return new Promise((resolve, reject) => {
const reader = new FileReader()
reader.onload = (e) => resolve(e.target?.result as string || '')
reader.onerror = (err) => reject(err)
reader.readAsDataURL(file)
})
}
async function onOcrFileChange(event: Event) {
const target = event.target as HTMLInputElement
const file = target.files?.[0]
@@ -587,46 +606,51 @@ async function onOcrFileChange(event: Event) {
ocrLoading.value = true
ocrProgress.value = 0
ocrStatusText.value = '인식 엔진 준비 중...'
ocrStatusText.value = '이미지 준비 중...'
ocrResultText.value = null
uploadedOcrImageBase64.value = ''
// Convert file to base64 synchronously
const reader = new FileReader()
reader.onload = (e) => {
uploadedOcrImageBase64.value = e.target?.result as string || ''
}
reader.readAsDataURL(file)
try {
const result = await Tesseract.recognize(
file,
'kor+eng',
{
logger: (m) => {
if (m && m.status) {
switch (m.status) {
case 'loading tesseract core':
ocrStatusText.value = '인식 엔진 불러오는 중...'
break
case 'initializing api':
ocrStatusText.value = '언어 데이터(한/영) 초기화 중...'
break
case 'recognizing text':
ocrStatusText.value = '글자 판독 및 텍스트 변환 중...'
break
default:
ocrStatusText.value = m.status
}
if (typeof m.progress === 'number') {
ocrProgress.value = Math.round(m.progress * 100)
const base64Data = await readFileAsDataURL(file)
uploadedOcrImageBase64.value = base64Data
if (useAiForOcr.value) {
ocrStatusText.value = 'AI 인식 진행 중 (Gemini)...'
ocrProgress.value = 50
const result = await QuizService.PerformGeminiOcr(base64Data)
ocrProgress.value = 100
ocrResultText.value = result || ''
} else {
ocrStatusText.value = '인식 엔진 로딩 중...'
const result = await Tesseract.recognize(
file,
'kor+eng',
{
logger: (m) => {
if (m && m.status) {
switch (m.status) {
case 'loading tesseract core':
ocrStatusText.value = '인식 엔진 불러오는 중...'
break
case 'initializing api':
ocrStatusText.value = '언어 데이터(한/영) 초기화 중...'
break
case 'recognizing text':
ocrStatusText.value = '글자 판독 및 텍스트 변환 중...'
break
default:
ocrStatusText.value = m.status
}
if (typeof m.progress === 'number') {
ocrProgress.value = Math.round(m.progress * 100)
}
}
}
}
}
)
ocrResultText.value = result.data.text || ''
)
ocrResultText.value = result.data.text || ''
}
} catch (err: any) {
console.error('OCR 실패:', err)
uploadedOcrImageBase64.value = ''