Skip to content

Commit

Permalink
Merge pull request #18 from LeBzul/feature/refacto
Browse files Browse the repository at this point in the history
Gestion de l'ocr sans widget
  • Loading branch information
LeBzul authored Aug 2, 2023
2 parents b467b63 + 76253b2 commit 4280c5c
Show file tree
Hide file tree
Showing 10 changed files with 293 additions and 162 deletions.
15 changes: 9 additions & 6 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:ocr_scan_text/ocr_scan_text.dart';
import 'package:ocr_scan_text_example/scan_all_module.dart';

void main() {
runApp(const MyApp());
Expand Down Expand Up @@ -31,13 +32,15 @@ class _MyAppState extends State<MyApp> {

Widget _buildLiveScan() {
return LiveScanWidget(
matchedResult: (ScanModule module, List<ScanResult> scanResult) {
for (var element in scanResult) {
print("matchedResult : ${element.cleanedText}");
// module.stop();
}
ocrTextResult: (ocrTextResult) {
ocrTextResult.mapResult.forEach((module, result) {
for (var element in result) {
print("matchedResult : ${element.cleanedText}");
// module.stop();
}
});
},
scanModules: [],
scanModules: [ScanAllModule()],
);
}
}
20 changes: 14 additions & 6 deletions example/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -133,10 +133,18 @@ packages:
dependency: transitive
description:
name: ffi
sha256: ed5337a5660c506388a9f012be0288fb38b49020ce2b45fe1f8b8323fe429f99
sha256: "13a6ccf6a459a125b3fcdb6ec73bd5ff90822e071207c663bfd1f70062d51d18"
url: "https://pub.dev"
source: hosted
version: "2.0.2"
version: "1.2.1"
file_picker:
dependency: transitive
description:
name: file_picker
sha256: "704259669b5e9cb24e15c11cfcf02caf5f20d30901b3916d60b6d1c2d647035f"
url: "https://pub.dev"
source: hosted
version: "4.6.1"
flutter:
dependency: "direct main"
description: flutter
Expand Down Expand Up @@ -291,10 +299,10 @@ packages:
dependency: transitive
description:
name: path_provider_windows
sha256: "1cb68ba4cd3a795033de62ba1b7b4564dace301f952de6bfb3cd91b202b6ee96"
sha256: a34ecd7fb548f8e57321fd8e50d865d266941b54e6c3b7758cf8f37c24116905
url: "https://pub.dev"
source: hosted
version: "2.1.7"
version: "2.0.7"
pdf_render:
dependency: transitive
description:
Expand Down Expand Up @@ -424,10 +432,10 @@ packages:
dependency: transitive
description:
name: win32
sha256: a6f0236dbda0f63aa9a25ad1ff9a9d8a4eaaa5012da0dc59d21afdb1dc361ca4
sha256: c0e3a4f7be7dae51d8f152230b86627e3397c1ba8c3fa58e63d44a9f3edc9cef
url: "https://pub.dev"
source: hosted
version: "3.1.4"
version: "2.6.1"
xdg_directories:
dependency: transitive
description:
Expand Down
2 changes: 1 addition & 1 deletion lib/ocr_scan/helper/pdf_helper.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class PDFHelper {
/// On prend que les 2 premiers page max, sinon c'est le bordel
for (int i = 1; i <= min(2, document.pageCount); i++) {
final page = await document.getPage(i);
int scaleUp = 5; // 4 is an arbitrary number, we enlarge the image to improve text detection
int scaleUp = 5; // 5 is an arbitrary number, we enlarge the image to improve text detection
final pageImage = await page.render(
width: page.width.toInt() * scaleUp,
height: page.height.toInt() * scaleUp,
Expand Down
8 changes: 4 additions & 4 deletions lib/ocr_scan/model/shape/trapezoid.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import 'dart:math';
import 'dart:ui';

import 'package:google_mlkit_text_recognition/google_mlkit_text_recognition.dart';
import 'package:ocr_scan_text/ocr_scan/widget/scan_widget.dart';
import 'package:ocr_scan_text/ocr_scan/services/ocr_scan_service.dart';

import '../recognizer_text/text_element.dart';

Expand All @@ -28,7 +28,7 @@ class Trapezoid {
/// The cornersPoints, with Android, have positions that differ from the main axes
/// X and Y are inverted and the 0 of the inverted axis is at imageSize.height
/// Just with camera
if (Platform.isAndroid && ScanWidget.actualMode == Mode.camera) {
if (Platform.isAndroid && OcrScanService.actualMode == Mode.camera) {
return Offset(
imageSize.height - point.y.toDouble(),
point.x.toDouble(),
Expand Down Expand Up @@ -186,7 +186,7 @@ class Trapezoid {
Size absoluteImageSize,
double adjustTranslate,
) {
double denominator = Platform.isIOS || (Platform.isAndroid && ScanWidget.actualMode == Mode.static)
double denominator = Platform.isIOS || (Platform.isAndroid && OcrScanService.actualMode == Mode.static)
? absoluteImageSize.width
: absoluteImageSize.height;
switch (rotation) {
Expand All @@ -206,7 +206,7 @@ class Trapezoid {
Size absoluteImageSize,
double adjustTranslate,
) {
double denominator = Platform.isIOS || (Platform.isAndroid && ScanWidget.actualMode == Mode.static)
double denominator = Platform.isIOS || (Platform.isAndroid && OcrScanService.actualMode == Mode.static)
? absoluteImageSize.height
: absoluteImageSize.width;
switch (rotation) {
Expand Down
231 changes: 231 additions & 0 deletions lib/ocr_scan/services/ocr_scan_service.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
import 'dart:io';
import 'dart:ui' as ui show Image;

import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:google_mlkit_text_recognition/google_mlkit_text_recognition.dart';
import 'package:image/image.dart' as img;
import 'package:path/path.dart' as path;
import 'package:pdf_render/pdf_render.dart';

import '../../ocr_scan_text.dart';
import '../helper/pdf_helper.dart';
import '../render/scan_renderer.dart';

enum Mode {
camera,
static,
}

class OcrScanService {
static Mode _actualMode = Mode.camera;
static Mode get actualMode => _actualMode;
List<ScanModule> scanModules;

/// MLKit text detection object
final TextRecognizer textRecognizer = TextRecognizer();

OcrScanService(
this.scanModules,
);

Future<OcrTextRecognizerResult?> startScanWithPhoto() async {
return _startStaticScanProcess(
await FilePicker.platform.pickFiles(
type: FileType.image,
allowCompression: false,
allowMultiple: false,
),
);
}

Future<OcrTextRecognizerResult?> startScanWithOpenFile() async {
return _startStaticScanProcess(
await FilePicker.platform.pickFiles(
type: FileType.custom,
allowCompression: false,
allowMultiple: false,
allowedExtensions: [
'png',
'jpg',
'pdf',
],
),
);
}

Future<OcrTextRecognizerResult?> _startStaticScanProcess(FilePickerResult? result) async {
if (result == null) {
return null;
}
String? path = result.files.first.path;
if (path == null) {
return null;
}
return await startScanProcess(
File(path),
);
}

Future<OcrTextRecognizerResult?> startScanProcess(
File file,
) async {
String extension = path.extension(file.path).toLowerCase();

assert(extension == '.pdf' || extension == '.png' || extension == '.jpg' || extension == '.jpeg');
if (extension == '.pdf') {
final PdfDocument document = await PdfDocument.openFile(
file.path,
);
return await _processStaticPDF(
document,
scanModules,
);
} else if (extension == '.png' || extension == '.jpg' || extension == '.jpeg') {
return await _processStaticImage(
file,
scanModules,
);
}
return null;
}

// Process image from camera stream
Future<OcrTextRecognizerResult?> _processStaticPDF(
PdfDocument pdfDocument,
List<ScanModule> scanModules,
) async {
ImagePDF? imagePDF = await PDFHelper.convertToPDFImage(pdfDocument);
if (imagePDF == null) {
return null;
}

ui.Image background = await decodeImageFromList(await imagePDF.file.readAsBytes());

return await processImage(
InputImage.fromFilePath(imagePDF.file.path),
Size(
background.width.toDouble(),
background.height.toDouble(),
),
background,
Mode.static,
scanModules,
null,
);
}

Future<OcrTextRecognizerResult?> _processStaticImage(
File file,
List<ScanModule> scanModules,
) async {
final cmd = img.Command()..decodeImageFile(file.path);
await cmd.executeThread();
img.Image? image = cmd.outputImage;
if (image == null) {
return null;
}
ui.Image background = await decodeImageFromList(await file.readAsBytes());

return await processImage(
InputImage.fromFilePath(file.path),
Size(
image.width.toDouble(),
image.height.toDouble(),
),
background,
Mode.static,
scanModules,
null,
);
}

/// Launch the search for results from the image for all the modules started
Future<OcrTextRecognizerResult?> processImage(
InputImage inputImage,
Size imageSize,
ui.Image? background,
Mode mode,
List<ScanModule> scanModules,
TextRecognizer? recognizer,
) async {
_actualMode = mode;
TextRecognizer textRecognizer = recognizer ?? TextRecognizer();

/// Ask MLKit to return the list of TextBlocks in the image
final recognizedText = await textRecognizer.processImage(inputImage);

/// create a global String corresponding to the texts found by MLKIt
String scannedText = '';
List<TextElement> textBlocks = [];
for (final textBlock in recognizedText.blocks) {
for (final element in textBlock.lines) {
for (final textBlock in element.elements) {
textBlocks.add(textBlock);
scannedText += " ${textBlock.text}";
}
}
}

/// Start the text search for each module
Map<ScanModule, List<MatchedCounter>> mapModule = <ScanModule, List<MatchedCounter>>{};
for (var scanModule in scanModules) {
if (!scanModule.started) {
continue;
}

/// Generate the results of each module
List<MatchedCounter> scanLines = await scanModule.generateResult(
recognizedText.blocks,
scannedText,
imageSize,
);

mapModule.putIfAbsent(
scanModule,
() => scanLines,
);
}

/// Create a ScanRenderer to display the visual rendering of the results found
var painter = ScanRenderer(
mapScanModules: mapModule,
imageRotation: inputImage.metadata?.rotation ?? InputImageRotation.rotation90deg,
imageSize: imageSize,
background: background,
);

Map<ScanModule, List<ScanResult>> mapResult = <ScanModule, List<ScanResult>>{};
mapModule.forEach((key, matchCounterList) {
List<ScanResult> list = matchCounterList
.where(
(matchCounter) => matchCounter.validated == true,
)
.map<ScanResult>((e) => e.scanResult)
.toList();

if (list.isNotEmpty) {
mapResult.putIfAbsent(key, () => list);
}
});

await textRecognizer.close();
if (mapResult.isEmpty) {
return null;
}

return OcrTextRecognizerResult(
CustomPaint(
painter: painter,
),
mapResult,
);
}
}

class OcrTextRecognizerResult {
CustomPaint customPaint;
Map<ScanModule, List<ScanResult>> mapResult;

OcrTextRecognizerResult(this.customPaint, this.mapResult);
}
6 changes: 3 additions & 3 deletions lib/ocr_scan/widget/live_scan_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ import 'package:ocr_scan_text/ocr_scan/widget/scan_widget.dart';

/// Widget allowing "live" scanning using the camera
class LiveScanWidget extends ScanWidget {
LiveScanWidget({
const LiveScanWidget({
super.key,
required super.scanModules,
required super.matchedResult,
required super.ocrTextResult,
super.respectRatio = false,
}) : super(mode: Mode.camera);
});

@override
LiveScanWidgetState createState() => LiveScanWidgetState();
Expand Down
Loading

0 comments on commit 4280c5c

Please sign in to comment.