-
Notifications
You must be signed in to change notification settings - Fork 0
feat: OCR 모듈 구성, 화면 및 기능 연동 #72
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 25 commits
Commits
Show all changes
31 commits
Select commit
Hold shift + click to select a range
24ec847
[BOOK-95] chore: core:ocr 모듈 추가
seoyoon513 17bfe34
[BOOK-95] chore: MLKit 의존성 추가
seoyoon513 187d939
Merge branch 'refs/heads/develop' into BOOK-95-feature/#29
seoyoon513 8a2c3f0
Merge branch 'develop' into BOOK-95-feature/#29
seoyoon513 5b18709
[BOOK-95] fix: BookItem 내부 패딩 영역 수정
seoyoon513 8728b06
Merge branch 'develop' into BOOK-95-feature/#29
seoyoon513 f848112
[BOOK-95] feat: Ocr 화면 UI 구현
seoyoon513 73bf085
[BOOK-95] core: CameraX 라이브러리 추가
seoyoon513 6e0b57b
[BOOK-95] feat: 카메라 런타임 권한 추가 및 카메라 Preview 구현
seoyoon513 b0f810e
[BOOK-95] feat: 실시간 텍스트 분석 객체 구현
seoyoon513 220fb21
[BOOK-95] feat: 정적 이미지 텍스트 분석 객체 구현 및 파라미터 타입 변경 (InputImage -> Image…
seoyoon513 1b24ca0
[BOOK-95] fix: StillTextAnalyzer와 LiveTextAnalyzer를 @AssistedInject +…
seoyoon513 6571758
[BOOK-95] feat: Ocr 기능 연동
seoyoon513 18fc591
[BOOK-95] feat: 인식된 문장을 선택하고 기록에 반영하는 기능 구현
seoyoon513 2b7ca0e
[BOOK-95] feat: 카메라 미리보기 회면에서 SystemBarColor 변경
seoyoon513 5b9abed
[BOOK-95] refactor: 불필요한 콜백 매개변수 삭제
seoyoon513 288fabc
[BOOK-95] feat: 카메라 커스텀 프레임 구현
seoyoon513 1cf563e
[BOOK-95] refactor: TextAnalyzer에서 예외가 있는 경우에만 로깅하도록 변경
seoyoon513 7ea032e
[BOOK-95] chore: code style check success
seoyoon513 daa3691
[BOOK-95] chore: 미사용 의존성 삭제
seoyoon513 5a28077
[BOOK-95] fix: Navigation BackStack pop 시 데이터 전달하도록 수정
seoyoon513 e86c7a1
[BOOK-95] feat: 인식된 문장이 없을 경우 인식 실패 로직 구현
seoyoon513 c3342b0
[BOOK-95] feat: 다시 촬영 시 다이얼로그 확인 요청 구현
seoyoon513 77a0155
[BOOK-95] feat: 수집된 문장을 마침표, 물음표, 느낌표 기준으로 단위 쪼개기
seoyoon513 c6f037f
[BOOK-95] chore: code style check success
seoyoon513 f9f7dea
[BOOK-95] refactor: 프레임마다 새로운 analyzer 인스턴스를 생성하지 않도록 present 함수 내에 선언
seoyoon513 d6a5332
[BOOK-95] refactor: 카메라 리소스와 imageAnalyzer의 생명주기를 Composable의 생명주기에 맞…
seoyoon513 48f4939
[BOOK-95] feat: 카메라 권한 요청 거절 시 대응 구현
seoyoon513 51516b8
[BOOK-95] refactor: data class에서 var로 선언한 변수 val로 변경 (불변성 원칙 위배)
seoyoon513 34d9234
[BOOK-95] refactor: TextAnalyzer에 CoroutineScope cancel 할 수 있는 로직 추가
seoyoon513 c7caa58
Merge branch 'develop' into BOOK-95-feature/#29
seoyoon513 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| /build |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| @file:Suppress("INLINE_FROM_HIGHER_PLATFORM") | ||
|
|
||
| plugins { | ||
| alias(libs.plugins.booket.android.library) | ||
| alias(libs.plugins.booket.android.hilt) | ||
| } | ||
|
|
||
| android { | ||
| namespace = "com.ninecraft.booket.core.ocr" | ||
| } | ||
|
|
||
| dependencies { | ||
| implementations( | ||
| libs.logger, | ||
| libs.androidx.camera.core, | ||
|
|
||
| libs.google.mlkit.text.recognition.korean, | ||
| ) | ||
| } |
75 changes: 75 additions & 0 deletions
75
core/ocr/src/main/kotlin/com/ninecraft/booket/core/ocr/analyzer/LiveTextAnalyzer.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| package com.ninecraft.booket.core.ocr.analyzer | ||
|
|
||
| import androidx.annotation.OptIn | ||
| import androidx.camera.core.ExperimentalGetImage | ||
| import androidx.camera.core.ImageProxy | ||
| import com.google.mlkit.vision.common.InputImage | ||
| import com.google.mlkit.vision.text.TextRecognizer | ||
| import com.orhanobut.logger.Logger | ||
| import dagger.assisted.Assisted | ||
| import dagger.assisted.AssistedFactory | ||
| import dagger.assisted.AssistedInject | ||
| import kotlinx.coroutines.CoroutineScope | ||
| import kotlinx.coroutines.Dispatchers | ||
| import kotlinx.coroutines.SupervisorJob | ||
| import kotlinx.coroutines.delay | ||
| import kotlinx.coroutines.launch | ||
| import kotlin.coroutines.resume | ||
| import kotlin.coroutines.suspendCoroutine | ||
|
|
||
| /** | ||
| * 실시간 카메라 스트림에서 프레임 단위로 텍스트 분석하는 Analyzer 클래스 | ||
| * | ||
| * ML Kit의 TextRecognizer를 사용하여 `ImageProxy` 객체로부터 텍스트를 추출한다 | ||
| * | ||
| * @param textRecognizer ML Kit의 TextRecognizer 인스턴스 | ||
| * @param onTextDetected 텍스트 인식 성공 시 호출되는 콜백 (인식된 전체 텍스트 전달) | ||
| * | ||
| * 안정적인 연속 프레임 분석을 위해 CoroutineScope에 [SupervisorJob]을 사용하여 | ||
| * 한 프레임 분석에서 예외가 발생해도 다음 프레임 분석에 영향을 주지 않도록 설계 | ||
| */ | ||
| class LiveTextAnalyzer @AssistedInject constructor( | ||
| private val textRecognizer: TextRecognizer, | ||
| @Assisted private val onTextDetected: (String) -> Unit, | ||
| ) : TextAnalyzer { | ||
|
|
||
| companion object { | ||
| const val THROTTLE_TIMEOUT_MS = 1_000L // 프레임 처리 간 인터벌 | ||
| } | ||
|
|
||
| private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob()) | ||
|
|
||
| @OptIn(ExperimentalGetImage::class) | ||
| override fun analyze(imageProxy: ImageProxy) { | ||
| scope.launch { | ||
| val mediaImage = imageProxy.image ?: run { imageProxy.close(); return@launch } | ||
| val inputImage = InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees) | ||
|
|
||
| suspendCoroutine { continuation -> | ||
| textRecognizer.process(inputImage) | ||
| .addOnCompleteListener { visionText -> | ||
| onTextDetected(visionText.result.text) | ||
| } | ||
| .addOnFailureListener { exception -> | ||
| Logger.e(exception.message ?: "Unknown error") | ||
| } | ||
| .addOnCompleteListener { | ||
| continuation.resume(Unit) | ||
| } | ||
| } | ||
| delay(THROTTLE_TIMEOUT_MS) | ||
| }.invokeOnCompletion { exception -> | ||
| if (exception != null) { | ||
| Logger.e(exception.message ?: "Unknown error") | ||
| } | ||
| imageProxy.close() | ||
| } | ||
| } | ||
|
|
||
| @AssistedFactory | ||
| interface Factory { | ||
| fun create( | ||
| onTextDetected: (String) -> Unit, | ||
| ): LiveTextAnalyzer | ||
| } | ||
| } | ||
70 changes: 70 additions & 0 deletions
70
core/ocr/src/main/kotlin/com/ninecraft/booket/core/ocr/analyzer/StillTextAnalyzer.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| package com.ninecraft.booket.core.ocr.analyzer | ||
|
|
||
| import androidx.annotation.OptIn | ||
| import androidx.camera.core.ExperimentalGetImage | ||
| import androidx.camera.core.ImageProxy | ||
| import com.google.mlkit.vision.common.InputImage | ||
| import com.google.mlkit.vision.text.TextRecognizer | ||
| import com.orhanobut.logger.Logger | ||
| import dagger.assisted.Assisted | ||
| import dagger.assisted.AssistedFactory | ||
| import dagger.assisted.AssistedInject | ||
| import kotlinx.coroutines.CoroutineScope | ||
| import kotlinx.coroutines.Dispatchers | ||
| import kotlinx.coroutines.launch | ||
| import kotlin.coroutines.resume | ||
| import kotlin.coroutines.suspendCoroutine | ||
|
|
||
| /** | ||
| * 정적인 카메라 이미지에서 텍스트를 분석하는 클래스 | ||
| * | ||
| * CameraX의 단일 ImageProxy 프레임을 받아 ML Kit을 통해 텍스트를 추출하고 결과를 콜백으로 전달한다. | ||
| * | ||
| * @param textRecognizer ML Kit의 TextRecognizer 인스턴스 | ||
| * @param onTextDetected 텍스트 인식 성공 시 호출되는 콜백 (인식된 전체 텍스트 전달) | ||
| * @param onFailure 인식 실패 시 호출되는 콜백 | ||
| * | ||
| * 분석이 끝난 후 반드시 imageProxy.close() 호출하여 리소스 해제 | ||
| */ | ||
| class StillTextAnalyzer @AssistedInject constructor( | ||
| private val textRecognizer: TextRecognizer, | ||
| @Assisted private val onTextDetected: (String) -> Unit, | ||
| @Assisted private val onFailure: () -> Unit, | ||
| ) : TextAnalyzer { | ||
|
|
||
| val scope = CoroutineScope(Dispatchers.IO) | ||
|
coderabbitai[bot] marked this conversation as resolved.
Outdated
|
||
|
|
||
| @OptIn(ExperimentalGetImage::class) | ||
| override fun analyze(imageProxy: ImageProxy) { | ||
| scope.launch { | ||
| val mediaImage = imageProxy.image ?: run { imageProxy.close(); return@launch } | ||
| val inputImage = InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees) | ||
|
|
||
| suspendCoroutine { continuation -> | ||
| textRecognizer.process(inputImage) | ||
| .addOnCompleteListener { visionText -> | ||
| onTextDetected(visionText.result.text) | ||
| } | ||
|
coderabbitai[bot] marked this conversation as resolved.
Outdated
|
||
| .addOnFailureListener { | ||
| onFailure() | ||
| } | ||
| .addOnCompleteListener { | ||
| continuation.resume(Unit) | ||
| } | ||
| } | ||
| }.invokeOnCompletion { exception -> | ||
| if (exception != null) { | ||
| Logger.e(exception.message ?: "Unknown error") | ||
| } | ||
| imageProxy.close() | ||
| } | ||
| } | ||
|
|
||
| @AssistedFactory | ||
| interface Factory { | ||
| fun create( | ||
| onTextDetected: (String) -> Unit, | ||
| onFailure: () -> Unit, | ||
| ): StillTextAnalyzer | ||
| } | ||
| } | ||
7 changes: 7 additions & 0 deletions
7
core/ocr/src/main/kotlin/com/ninecraft/booket/core/ocr/analyzer/TextAnalyzer.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| package com.ninecraft.booket.core.ocr.analyzer | ||
|
|
||
| import androidx.camera.core.ImageProxy | ||
|
|
||
| interface TextAnalyzer { | ||
| fun analyze(imageProxy: ImageProxy) | ||
| } |
20 changes: 20 additions & 0 deletions
20
core/ocr/src/main/kotlin/com/ninecraft/booket/core/ocr/di/OcrModule.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| package com.ninecraft.booket.core.ocr.di | ||
|
|
||
| import com.google.mlkit.vision.text.TextRecognition | ||
| import com.google.mlkit.vision.text.TextRecognizer | ||
| import com.google.mlkit.vision.text.korean.KoreanTextRecognizerOptions | ||
| import dagger.Module | ||
| import dagger.Provides | ||
| import dagger.hilt.InstallIn | ||
| import dagger.hilt.components.SingletonComponent | ||
| import javax.inject.Singleton | ||
|
|
||
| @Module | ||
| @InstallIn(SingletonComponent::class) | ||
| object OcrModule { | ||
|
|
||
| @Provides | ||
| @Singleton | ||
| fun provideTextRecognizer(): TextRecognizer = | ||
| TextRecognition.getClient(KoreanTextRecognizerOptions.Builder().build()) | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.