blob: 8f6e1d8c2e1d83e8c5e177a2bfc1f25d60500b1d [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
part of firebase_ml_vision;
/// Detector for performing optical character recognition(OCR) on an input image.
///
/// A text recognizer is created via `textRecognizer()` in [FirebaseVision]:
///
/// ```dart
/// final FirebaseVisionImage image =
/// FirebaseVisionImage.fromFilePath('path/to/file');
///
/// final TextRecognizer textRecognizer =
/// FirebaseVision.instance.textRecognizer();
///
/// final List<VisionText> recognizedText =
/// await textRecognizer.processImage(image);
/// ```
class TextRecognizer {
TextRecognizer._();
/// Detects [VisionText] from a [FirebaseVisionImage].
Future<VisionText> processImage(FirebaseVisionImage visionImage) async {
final Map<dynamic, dynamic> reply =
// TODO(amirh): remove this on when the invokeMethod update makes it to stable Flutter.
// https://github.com/flutter/flutter/issues/26431
// ignore: strong_mode_implicit_dynamic_method
await FirebaseVision.channel.invokeMethod(
'TextRecognizer#processImage',
<String, dynamic>{
'options': <String, dynamic>{},
}..addAll(visionImage._serialize()),
);
return VisionText._(reply);
}
}
/// Recognized text in an image.
class VisionText {
VisionText._(Map<dynamic, dynamic> data)
: text = data['text'],
blocks = List<TextBlock>.unmodifiable(data['blocks']
.map<TextBlock>((dynamic block) => TextBlock._(block)));
/// String representation of the recognized text.
final String text;
/// All recognized text broken down into individual blocks/paragraphs.
final List<TextBlock> blocks;
}
/// Detected language from text recognition.
class RecognizedLanguage {
RecognizedLanguage._(dynamic data) : languageCode = data['languageCode'];
/// The BCP-47 language code, such as, en-US or sr-Latn. For more information,
/// see http://www.unicode.org/reports/tr35/#Unicode_locale_identifier.
final String languageCode;
}
/// Abstract class representing dimensions of recognized text in an image.
abstract class TextContainer {
TextContainer._(Map<dynamic, dynamic> data)
: boundingBox = data['left'] != null
? Rect.fromLTWH(
data['left'],
data['top'],
data['width'],
data['height'],
)
: null,
confidence = data['confidence'],
cornerPoints = List<Offset>.unmodifiable(
data['points'].map<Offset>((dynamic point) => Offset(
point[0],
point[1],
))),
recognizedLanguages = List<RecognizedLanguage>.unmodifiable(
data['recognizedLanguages'].map<RecognizedLanguage>(
(dynamic language) => RecognizedLanguage._(language))),
text = data['text'];
/// Axis-aligned bounding rectangle of the detected text.
///
/// The point (0, 0) is defined as the upper-left corner of the image.
///
/// Could be null even if text is found.
final Rect boundingBox;
/// The confidence of the recognized text block.
///
/// The value is null for all text recognizers except for cloud text
/// recognizers.
final double confidence;
/// The four corner points in clockwise direction starting with top-left.
///
/// Due to the possible perspective distortions, this is not necessarily a
/// rectangle. Parts of the region could be outside of the image.
///
/// Could be empty even if text is found.
final List<Offset> cornerPoints;
/// All detected languages from recognized text.
///
/// On-device text recognizers only detect Latin-based languages, while cloud
/// text recognizers can detect multiple languages. If no languages are
/// recognized, the list is empty.
final List<RecognizedLanguage> recognizedLanguages;
/// The recognized text as a string.
///
/// Returned in reading order for the language. For Latin, this is top to
/// bottom within a Block, and left-to-right within a Line.
final String text;
}
/// A block of text (think of it as a paragraph) as deemed by the OCR engine.
class TextBlock extends TextContainer {
TextBlock._(Map<dynamic, dynamic> block)
: lines = List<TextLine>.unmodifiable(
block['lines'].map<TextLine>((dynamic line) => TextLine._(line))),
super._(block);
/// The contents of the text block, broken down into individual lines.
final List<TextLine> lines;
}
/// Represents a line of text.
class TextLine extends TextContainer {
TextLine._(Map<dynamic, dynamic> line)
: elements = List<TextElement>.unmodifiable(line['elements']
.map<TextElement>((dynamic element) => TextElement._(element))),
super._(line);
/// The contents of this line, broken down into individual elements.
final List<TextElement> elements;
}
/// Roughly equivalent to a space-separated "word."
///
/// The API separates elements into words in most Latin languages, but could
/// separate by characters in others.
///
/// If a word is split between two lines by a hyphen, each part is encoded as a
/// separate element.
class TextElement extends TextContainer {
TextElement._(Map<dynamic, dynamic> element) : super._(element);
}