Skip to content

Commit 30ab43e

Browse files
committed
[BOOK-275] feat: CloudVision API 정의
1 parent 1e15367 commit 30ab43e

File tree

7 files changed

+184
-0
lines changed

7 files changed

+184
-0
lines changed

core/ocr/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
plugins {
44
alias(libs.plugins.booket.android.library)
5+
alias(libs.plugins.booket.android.retrofit)
56
alias(libs.plugins.booket.android.hilt)
67
}
78

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package com.ninecraft.booket.core.ocr.analyzer
2+
3+
import com.ninecraft.booket.core.ocr.model.AnnotateImageRequest
4+
import com.ninecraft.booket.core.ocr.model.CloudVisionRequest
5+
import com.ninecraft.booket.core.ocr.model.Feature
6+
import com.ninecraft.booket.core.ocr.model.ImageContext
7+
import com.ninecraft.booket.core.ocr.model.VisionImage
8+
import com.ninecraft.booket.core.ocr.service.CloudVisionService
9+
import com.orhanobut.logger.Logger
10+
import javax.inject.Inject
11+
12+
class CloudOcrRecognizer @Inject constructor(
13+
private val service: CloudVisionService,
14+
) {
15+
suspend fun recognizeText(base64Image: String) {
16+
try {
17+
val request = CloudVisionRequest(
18+
requests = listOf(
19+
AnnotateImageRequest(
20+
image = VisionImage(base64Image),
21+
features = listOf(Feature(type = "TEXT_DETECTION")),
22+
imageContext = ImageContext(languageHints = null),
23+
),
24+
),
25+
)
26+
service.annotate(
27+
key = "", // local properties 에서 key 가져오기
28+
body = request,
29+
)
30+
} catch (e: Exception) {
31+
Logger.e("${e.message}")
32+
}
33+
}
34+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package com.ninecraft.booket.core.ocr.di
2+
3+
import com.ninecraft.booket.core.ocr.service.CloudVisionService
4+
import dagger.Module
5+
import dagger.Provides
6+
import dagger.hilt.InstallIn
7+
import dagger.hilt.components.SingletonComponent
8+
import kotlinx.serialization.json.Json
9+
import okhttp3.MediaType.Companion.toMediaType
10+
import okhttp3.OkHttpClient
11+
import okhttp3.logging.HttpLoggingInterceptor
12+
import retrofit2.Retrofit
13+
import retrofit2.converter.kotlinx.serialization.asConverterFactory
14+
import java.util.concurrent.TimeUnit
15+
import javax.inject.Singleton
16+
17+
18+
private const val MaxTimeoutMillis = 15_000L
19+
20+
private val jsonRule = Json {
21+
// 기본값도 JSON에 포함하여 직렬화
22+
encodeDefaults = true
23+
// JSON에 정의되지 않은 키는 무시 (역직렬화 시 에러 방지)
24+
ignoreUnknownKeys = true
25+
// JSON을 보기 좋게 들여쓰기하여 포맷팅
26+
prettyPrint = true
27+
// 엄격하지 않은 파싱 (따옴표 없는 키, 후행 쉼표 등 허용)
28+
isLenient = true
29+
}
30+
31+
private val jsonConverterFactory = jsonRule.asConverterFactory("application/json".toMediaType())
32+
33+
@Module
34+
@InstallIn(SingletonComponent::class)
35+
object CloudVisionNetworkModule {
36+
37+
@Provides
38+
@Singleton
39+
@CloudVisionOkHttp
40+
fun provideOkHttp(): OkHttpClient {
41+
val log = HttpLoggingInterceptor().apply {
42+
level = HttpLoggingInterceptor.Level.BASIC
43+
}
44+
return OkHttpClient.Builder()
45+
.addInterceptor(log)
46+
.addInterceptor { chain ->
47+
val req = chain.request().newBuilder()
48+
.build()
49+
chain.proceed(req)
50+
}
51+
.connectTimeout(MaxTimeoutMillis, TimeUnit.MILLISECONDS)
52+
.readTimeout(MaxTimeoutMillis, TimeUnit.MILLISECONDS)
53+
.writeTimeout(MaxTimeoutMillis, TimeUnit.MILLISECONDS)
54+
.build()
55+
}
56+
57+
@Provides
58+
@Singleton
59+
@CloudVisionRetrofit
60+
fun provideRetrofit(
61+
@CloudVisionOkHttp okHttpClient: OkHttpClient,
62+
): Retrofit {
63+
return Retrofit.Builder()
64+
.baseUrl("https://vision.googleapis.com/")
65+
.client(okHttpClient)
66+
.addConverterFactory(jsonConverterFactory)
67+
.build()
68+
}
69+
70+
@Provides
71+
@Singleton
72+
fun provideVisionApi(@CloudVisionRetrofit retrofit: Retrofit): CloudVisionService =
73+
retrofit.create(CloudVisionService::class.java)
74+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.ninecraft.booket.core.ocr.di
2+
3+
import javax.inject.Qualifier
4+
5+
@Qualifier
6+
@Retention(AnnotationRetention.BINARY)
7+
annotation class CloudVisionOkHttp
8+
9+
@Qualifier
10+
@Retention(AnnotationRetention.BINARY)
11+
annotation class CloudVisionRetrofit
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package com.ninecraft.booket.core.ocr.model
2+
3+
import kotlinx.serialization.Serializable
4+
5+
6+
@Serializable
7+
data class CloudVisionRequest(
8+
val requests: List<AnnotateImageRequest>,
9+
)
10+
11+
@Serializable
12+
data class AnnotateImageRequest(
13+
val image: VisionImage,
14+
val features: List<Feature>,
15+
val imageContext: ImageContext? = null
16+
)
17+
18+
@Serializable
19+
data class VisionImage(
20+
val content: String,
21+
)
22+
23+
@Serializable
24+
data class Feature(
25+
val type: String = "TEXT_DETECTION",
26+
)
27+
28+
@Serializable
29+
data class ImageContext(
30+
val languageHints: List<String>? = null
31+
)
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.ninecraft.booket.core.ocr.model
2+
3+
import kotlinx.serialization.Serializable
4+
5+
@Serializable
6+
data class CloudVisionResponse(
7+
val response: List<AnnotateImageResponse>,
8+
)
9+
10+
@Serializable
11+
data class AnnotateImageResponse(
12+
val fullTextAnnotation: FullTextAnnotation? = null,
13+
)
14+
15+
@Serializable
16+
data class FullTextAnnotation(
17+
val text: String? = null,
18+
)
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.ninecraft.booket.core.ocr.service
2+
3+
import com.ninecraft.booket.core.ocr.model.CloudVisionRequest
4+
import com.ninecraft.booket.core.ocr.model.CloudVisionResponse
5+
import retrofit2.http.Body
6+
import retrofit2.http.POST
7+
import retrofit2.http.Query
8+
9+
interface CloudVisionService {
10+
@POST("v1/images:annotate")
11+
suspend fun annotate(
12+
@Query("key") key: String,
13+
@Body body: CloudVisionRequest
14+
): CloudVisionResponse
15+
}

0 commit comments

Comments
 (0)