ML Kit의 디지털 잉크 인식을 사용하면 스케치를 분류할 수 있습니다.
<ph type="x-smartling-placeholder">사용해 보기
- 샘플 앱을 사용하여 이 API의 사용 예를 참조하세요.
시작하기 전에
<ph type="x-smartling-placeholder">- 프로젝트 수준
build.gradle
파일의buildscript
및allprojects
섹션에 Google의 Maven 저장소가 포함되어야 합니다. - 모듈의 앱 수준 Gradle 파일(일반적으로
app/build.gradle
)에 ML Kit Android 라이브러리의 종속 항목을 추가합니다.
dependencies {
// ...
implementation 'com.google.mlkit:digital-ink-recognition:18.1.0'
}
이제 Ink
객체에서 텍스트 인식을 시작할 수 있습니다.
Ink
객체 빌드
Ink
객체를 빌드하는 기본 방법은 터치스크린에 객체를 그리는 것입니다. 사용 설정됨
Android에서는
캔버스:
사용할 수 있습니다 터치 이벤트 핸들러는 다음 코드 스니펫에 표시된 addNewTouchEvent()
메서드를 호출하여 사용자가 Ink
객체에 그리는 획의 점을 저장해야 합니다.
이 일반적인 패턴은 다음 코드 스니펫에 나와 있습니다. 더 완전한 예시는 ML Kit 빠른 시작 샘플을 참고하세요.
Kotlin
var inkBuilder = Ink.builder() lateinit var strokeBuilder: Ink.Stroke.Builder // Call this each time there is a new event. fun addNewTouchEvent(event: MotionEvent) { val action = event.actionMasked val x = event.x val y = event.y var t = System.currentTimeMillis() // If your setup does not provide timing information, you can omit the // third paramater (t) in the calls to Ink.Point.create when (action) { MotionEvent.ACTION_DOWN -> { strokeBuilder = Ink.Stroke.builder() strokeBuilder.addPoint(Ink.Point.create(x, y, t)) } MotionEvent.ACTION_MOVE -> strokeBuilder!!.addPoint(Ink.Point.create(x, y, t)) MotionEvent.ACTION_UP -> { strokeBuilder.addPoint(Ink.Point.create(x, y, t)) inkBuilder.addStroke(strokeBuilder.build()) } else -> { // Action not relevant for ink construction } } } ... // This is what to send to the recognizer. val ink = inkBuilder.build()
자바
Ink.Builder inkBuilder = Ink.builder(); Ink.Stroke.Builder strokeBuilder; // Call this each time there is a new event. public void addNewTouchEvent(MotionEvent event) { float x = event.getX(); float y = event.getY(); long t = System.currentTimeMillis(); // If your setup does not provide timing information, you can omit the // third paramater (t) in the calls to Ink.Point.create int action = event.getActionMasked(); switch (action) { case MotionEvent.ACTION_DOWN: strokeBuilder = Ink.Stroke.builder(); strokeBuilder.addPoint(Ink.Point.create(x, y, t)); break; case MotionEvent.ACTION_MOVE: strokeBuilder.addPoint(Ink.Point.create(x, y, t)); break; case MotionEvent.ACTION_UP: strokeBuilder.addPoint(Ink.Point.create(x, y, t)); inkBuilder.addStroke(strokeBuilder.build()); strokeBuilder = null; break; } } ... // This is what to send to the recognizer. Ink ink = inkBuilder.build();
DigitalInkRecognizer 인스턴스 가져오기
인식을 실행하려면 Ink
인스턴스를 DigitalInkRecognizer
객체로 전송합니다. 아래 코드는 BCP-47 태그에서 이러한 인식기를 인스턴스화하는 방법을 보여줍니다.
Kotlin
// Specify the recognition model for a language var modelIdentifier: DigitalInkRecognitionModelIdentifier try { modelIdentifier = DigitalInkRecognitionModelIdentifier.fromLanguageTag("en-US") } catch (e: MlKitException) { // language tag failed to parse, handle error. } if (modelIdentifier == null) { // no model was found, handle error. } var model: DigitalInkRecognitionModel = DigitalInkRecognitionModel.builder(modelIdentifier).build() // Get a recognizer for the language var recognizer: DigitalInkRecognizer = DigitalInkRecognition.getClient( DigitalInkRecognizerOptions.builder(model).build())
자바
// Specify the recognition model for a language DigitalInkRecognitionModelIdentifier modelIdentifier; try { modelIdentifier = DigitalInkRecognitionModelIdentifier.fromLanguageTag("en-US"); } catch (MlKitException e) { // language tag failed to parse, handle error. } if (modelIdentifier == null) { // no model was found, handle error. } DigitalInkRecognitionModel model = DigitalInkRecognitionModel.builder(modelIdentifier).build(); // Get a recognizer for the language DigitalInkRecognizer recognizer = DigitalInkRecognition.getClient( DigitalInkRecognizerOptions.builder(model).build());
Ink
객체 처리
Kotlin
recognizer.recognize(ink) .addOnSuccessListener { result: RecognitionResult -> // `result` contains the recognizer's answers as a RecognitionResult. // Logs the text from the top candidate. Log.i(TAG, result.candidates[0].text) } .addOnFailureListener { e: Exception -> Log.e(TAG, "Error during recognition: $e") }
자바
recognizer.recognize(ink) .addOnSuccessListener( // `result` contains the recognizer's answers as a RecognitionResult. // Logs the text from the top candidate. result -> Log.i(TAG, result.getCandidates().get(0).getText())) .addOnFailureListener( e -> Log.e(TAG, "Error during recognition: " + e));
위의 샘플 코드는 인식 모델이 이미 생성되었다고 가정합니다. 다음 섹션에 설명된 대로 다운로드될 수 있습니다
모델 다운로드 관리
디지털 잉크 인식 API는 수백 개의 언어를 지원하지만
인식하기 전에 일부 데이터를 다운로드해야 합니다. 주변
언어당 20MB의 저장용량이 필요합니다. 이 작업은 RemoteModelManager
객체에서 처리합니다.
새 모델 다운로드
Kotlin
import com.google.mlkit.common.model.DownloadConditions import com.google.mlkit.common.model.RemoteModelManager var model: DigitalInkRecognitionModel = ... val remoteModelManager = RemoteModelManager.getInstance() remoteModelManager.download(model, DownloadConditions.Builder().build()) .addOnSuccessListener { Log.i(TAG, "Model downloaded") } .addOnFailureListener { e: Exception -> Log.e(TAG, "Error while downloading a model: $e") }
자바
import com.google.mlkit.common.model.DownloadConditions; import com.google.mlkit.common.model.RemoteModelManager; DigitalInkRecognitionModel model = ...; RemoteModelManager remoteModelManager = RemoteModelManager.getInstance(); remoteModelManager .download(model, new DownloadConditions.Builder().build()) .addOnSuccessListener(aVoid -> Log.i(TAG, "Model downloaded")) .addOnFailureListener( e -> Log.e(TAG, "Error while downloading a model: " + e));
모델이 이미 다운로드되었는지 확인
Kotlin
var model: DigitalInkRecognitionModel = ... remoteModelManager.isModelDownloaded(model)
자바
DigitalInkRecognitionModel model = ...; remoteModelManager.isModelDownloaded(model);
다운로드한 모델 삭제
기기의 저장공간에서 모델을 삭제하면 공간이 확보됩니다.
Kotlin
var model: DigitalInkRecognitionModel = ... remoteModelManager.deleteDownloadedModel(model) .addOnSuccessListener { Log.i(TAG, "Model successfully deleted") } .addOnFailureListener { e: Exception -> Log.e(TAG, "Error while deleting a model: $e") }
Java
DigitalInkRecognitionModel model = ...; remoteModelManager.deleteDownloadedModel(model) .addOnSuccessListener( aVoid -> Log.i(TAG, "Model successfully deleted")) .addOnFailureListener( e -> Log.e(TAG, "Error while deleting a model: " + e));
텍스트 인식 정확도를 개선하기 위한 도움말
텍스트 인식의 정확성은 언어에 따라 다를 수 있습니다. 정확성은 작문 스타일에 따라 달라집니다. 디지털 잉크 인식은 다양한 종류의 필기 스타일을 처리하도록 학습되지만 결과는 사용자마다 다를 수 있습니다.
다음은 텍스트 인식기의 정확성을 개선하는 방법입니다. 이 기법은 그림 이모티콘, 자동 그리기, 도형의 그리기 분류기에 적용되지 않습니다.
쓰기 영역
많은 애플리케이션에는 사용자 입력을 위한 쓰기 영역이 잘 정의되어 있습니다. 기호의 의미는 이는 텍스트가 포함된 쓰기 영역의 크기를 기준으로 한 크기에 의해 부분적으로 결정됩니다. 예를 들어 소문자와 대문자 'o'의 차이를 보면 또는 'c', 쉼표와 a 슬래시를 사용합니다.
인식기에 쓰기 영역의 너비와 높이를 알려주면 정확성을 개선할 수 있습니다. 그러나 인식기는 작성 영역에 텍스트가 한 줄만 포함되어 있다고 가정합니다. 만약 쓰기 영역이 사용자가 두 줄 이상을 쓸 수 있을 만큼 충분히 크면 더 높이가 가장 잘 추정되는 높이인 WriteArea를 전달하여 결과를 줄일 수 있습니다 인식기에 전달하는 WritingArea 객체는 화면의 실제 필기 영역과 정확히 일치할 필요가 없습니다. 이런 방식으로 WriteArea 높이를 변경 특정 언어에서 더 나은 결과를 얻을 수 있습니다
쓰기 영역을 지정할 때 쓰기 영역의 너비와 높이를 획과 같은 단위로 지정합니다. 좌표입니다. x,y 좌표 인수에는 단위 요구사항이 없습니다. API는 따라서 중요한 것은 획의 상대적 크기와 위치뿐입니다. 언제든지 시스템에 적합한 배율로 좌표를 전달할 수 있습니다.
사전 맥락
사전 컨텍스트는 개발자가 작성한 Ink
에서 획 바로 앞에 오는 텍스트입니다.
인식할 수 있습니다. 사전 컨텍스트에 관해 알려주면 인식기에 도움이 될 수 있습니다.
예를 들어 필기체의 'n'과 'u'는 서로 혼동되는 경우가 많습니다. 사용자가 이미 'arg'라는 부분 단어를 입력했다면 'ument' 또는 'nment'로 인식될 수 있는 획을 계속할 수 있습니다. 사전 컨텍스트 'arg'를 지정하면 모호성이 해결됩니다. 'argument'라는 단어가 'argnment'보다 더 가능성이 높기 때문입니다.
전 컨텍스트는 인식기가 단어 사이의 공백을 식별하는 데도 도움이 됩니다. 공백 문자를 입력할 수는 있지만 그릴 수는 없으므로 인식기는 한 단어가 끝나고 다음 단어가 시작되는 시점을 어떻게 결정할 수 있을까요? 사용자가 이미 'hello'를 입력하고 'world'를 계속 입력하는 경우 사전 컨텍스트 없이 인식기는 'world' 문자열을 반환합니다. 그러나 'hello'를 호출하는 경우 모델은 ' 'hello'라고 되어 있으므로 World" 'helloword'보다 더 의미가 있습니다.
문맥에 맞는 사전 문자열을 최대한 길게(영문 기준 최대 20자) 합니다. 문자열이 더 긴 경우 인식기는 마지막 20자만 사용합니다.
아래의 코드 샘플은 쓰기 영역을 정의하고
사전 컨텍스트를 지정하는 RecognitionContext
객체
Kotlin
var preContext : String = ...; var width : Float = ...; var height : Float = ...; val recognitionContext : RecognitionContext = RecognitionContext.builder() .setPreContext(preContext) .setWritingArea(WritingArea(width, height)) .build() recognizer.recognize(ink, recognitionContext)
자바
String preContext = ...; float width = ...; float height = ...; RecognitionContext recognitionContext = RecognitionContext.builder() .setPreContext(preContext) .setWritingArea(new WritingArea(width, height)) .build(); recognizer.recognize(ink, recognitionContext);
획 정렬
인식 정확도는 획의 순서에 민감합니다. 인식기는 스트로크가 사람들이 자연스럽게 쓰는 순서대로 일어납니다. 예를 들어 영어의 경우 왼쪽에서 오른쪽으로 모든 케이스 예를 들어 마지막 단어로 시작하는 영어 문장을 작성하는 경우 덜 정확한 결과를 제공합니다.
또 다른 예는 Ink
중간에 있는 단어가 삭제되고 다른 단어로 대체되는 경우입니다. 수정사항이 문장 중간에 있을 수 있지만 수정사항의 획은 획 시퀀스의 끝에 있습니다.
이 경우 새로 작성된 단어를 API에 별도로 전송하고
결과를 자체 로직을 사용하여 이전 인식으로 반환합니다.
모호한 도형 처리
인식기에 제공된 도형의 의미가 모호한 경우가 있습니다. 대상 예를 들어 가장자리가 매우 둥근 직사각형은 직사각형이나 타원으로 보일 수 있습니다.
이러한 불분명한 케이스는 인식 점수가 있는 경우 이를 사용하여 처리할 수 있습니다. 단
셰이프 분류기가 점수를 제공합니다. 모델이 매우 확신할 경우 상위 결과의 점수는
훨씬 나아졌습니다. 불확실성이 있는 경우 상위 두 결과의 점수가 비슷합니다. 또한 도형 분류기는 전체 Ink
를 단일 도형으로 해석합니다. 예를 들어 Ink
에 직사각형이 있고 그 옆에는 타원이 포함되어 있는 경우
인식기는 둘 중 하나 (또는 완전히 다른 것)를
단일 인식 후보가 두 개의 셰이프를 나타낼 수 없기 때문입니다.