Browse Source

working on multi scan improvements

pull/76/head
Khoren Markosyan 2 years ago
parent
commit
d150fc6a12
  1. 4
      .pubignore
  2. 108
      example/lib/main.dart
  3. 6
      lib/flutter_zxing.dart
  4. 8
      lib/generated_bindings.dart
  5. 6
      lib/src/logic/camera_stream.dart
  6. 4
      lib/src/models/params.dart
  7. 5
      lib/src/models/position.dart
  8. 49
      lib/src/ui/reader_widget.dart
  9. 26
      lib/src/utils/extentions.dart
  10. 18
      lib/src/utils/isolate_utils.dart
  11. 11
      lib/zxing_mobile.dart
  12. 7
      lib/zxing_web.dart
  13. 7
      src/native_zxing.cpp
  14. 4
      src/native_zxing.h
  15. 2
      zxscanner/pubspec.lock
  16. 2
      zxscanner/pubspec.yaml

4
.pubignore

@ -28,8 +28,10 @@
.packages
build/
# Ignore Example and ZXScanner folders
# Ignore ZXScanner folders
zxscanner/
# Ignore ZXing-CPP unneeded files
src/zxing/*
!src/zxing/CMakeLists.txt
!src/zxing/core/

108
example/lib/main.dart

@ -1,10 +1,12 @@
import 'dart:ui';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_zxing/flutter_zxing.dart';
import 'package:image_picker/image_picker.dart';
void main() {
zx.setLogEnabled(kDebugMode);
zx.setLogEnabled(false);
runApp(const MyApp());
}
@ -36,6 +38,7 @@ class _DemoPageState extends State<DemoPage> {
Uint8List? createdCodeBytes;
Code? result;
List<Code> multiResult = [];
bool showDebugInfo = true;
int successScans = 0;
@ -77,8 +80,26 @@ class _DemoPageState extends State<DemoPage> {
ReaderWidget(
onScan: _onScanSuccess,
onScanFailure: _onScanFailure,
tryInverted: true,
onMultiScan: _onMultiScanSuccess,
onMultiScanFailure: _onMultiScanFailure,
isMultiScan: true,
// showScannerOverlay: false,
// scanDelay: const Duration(milliseconds: 0),
// tryInverted: true,
),
// show multi results as rectangles
// if (multiResult.isNotEmpty)
// Positioned.fill(
// child: CustomPaint(
// painter: MultiScanPainter(
// codes: multiResult,
// size: Size(
// MediaQuery.of(context).size.width,
// MediaQuery.of(context).size.height,
// ),
// ),
// ),
// ),
ScanFromGalleryWidget(
onScan: _onScanSuccess,
onScanFailure: _onScanFailure,
@ -138,6 +159,23 @@ class _DemoPageState extends State<DemoPage> {
}
}
_onMultiScanSuccess(List<Code> codes) {
setState(() {
successScans++;
multiResult = codes;
});
}
_onMultiScanFailure(List<Code> codes) {
setState(() {
failedScans++;
multiResult = codes;
});
if (result?.error?.isNotEmpty == true) {
_showMessage(context, 'Error: ${codes.first.error}');
}
}
_showMessage(BuildContext context, String message) {
ScaffoldMessenger.of(context).hideCurrentSnackBar();
ScaffoldMessenger.of(context).showSnackBar(
@ -286,9 +324,7 @@ class DebugInfoWidget extends StatelessWidget {
}
class UnsupportedPlatformWidget extends StatelessWidget {
const UnsupportedPlatformWidget({
Key? key,
}) : super(key: key);
const UnsupportedPlatformWidget({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
@ -300,3 +336,65 @@ class UnsupportedPlatformWidget extends StatelessWidget {
);
}
}
class MultiResultWidget extends StatelessWidget {
const MultiResultWidget({
super.key,
this.results = const [],
});
final List<Code> results;
@override
Widget build(BuildContext context) {
return Container();
}
}
class MultiScanPainter extends CustomPainter {
MultiScanPainter({
required this.codes,
required this.size,
// required this.scale,
// required this.offset,
});
final List<Code> codes;
final Size size;
final double scale = 1;
final Offset offset = Offset.zero;
@override
void paint(Canvas canvas, Size size) {
final Paint paint = Paint()
..color = Colors.green
..strokeWidth = 2
..style = PaintingStyle.stroke;
for (final Code code in codes) {
final position = code.position;
if (position == null) {
continue;
}
// position to points
final List<Offset> points = positionToPoints(position);
// debugPrint('w: ${position.imageWidth} h: ${position.imageHeight}');
// print(points);
canvas.drawPoints(PointMode.polygon, points, paint);
}
}
@override
bool shouldRepaint(MultiScanPainter oldDelegate) {
return true;
}
List<Offset> positionToPoints(Position pos) {
return [
Offset(pos.topLeftX.toDouble(), pos.topLeftY.toDouble()),
Offset(pos.topRightX.toDouble(), pos.topRightY.toDouble()),
Offset(pos.bottomRightX.toDouble(), pos.bottomRightY.toDouble()),
Offset(pos.bottomLeftX.toDouble(), pos.bottomLeftY.toDouble()),
];
}
}

6
lib/flutter_zxing.dart

@ -38,6 +38,12 @@ abstract class Zxing {
DecodeParams? params,
});
/// Reads barcodes from the camera
Future<List<Code>> processCameraImageMulti(
CameraImage image, {
DecodeParams? params,
});
/// Reads barcode from String image path
Future<Code?> readBarcodeImagePathString(
String path, {

8
lib/generated_bindings.dart

@ -175,6 +175,14 @@ class GeneratedBindings {
/// @brief Pos is a position of a barcode in a image.
class Pos extends ffi.Struct {
/// < The width of the image
@ffi.Int()
external int imageWidth;
/// < The height of the image
@ffi.Int()
external int imageHeight;
/// < x coordinate of top left corner of barcode
@ffi.Int()
external int topLeftX;

6
lib/src/logic/camera_stream.dart

@ -11,17 +11,17 @@ Future<void> zxingStartCameraProcessing() async {
/// Stops reading barcode from the camera
void zxingStopCameraProcessing() => isolateUtils?.stopReadingBarcode();
Future<Code> zxingProcessCameraImage(
Future<dynamic> zxingProcessCameraImage(
CameraImage image, {
DecodeParams? params,
}) async {
final IsolateData isolateData = IsolateData(image, params ?? DecodeParams());
final Code result = await _inference(isolateData);
final dynamic result = await _inference(isolateData);
return result;
}
/// Runs inference in another isolate
Future<Code> _inference(IsolateData isolateData) async {
Future<dynamic> _inference(IsolateData isolateData) async {
final ReceivePort responsePort = ReceivePort();
isolateUtils?.sendPort
?.send(isolateData..responsePort = responsePort.sendPort);

4
lib/src/models/params.dart

@ -12,6 +12,7 @@ class DecodeParams {
this.tryRotate = true,
this.tryInverted = false,
this.maxSize = 512,
this.isMultiScan = false,
});
// Specify a set of BarcodeFormats that should be searched for, the default is all supported formats.
@ -34,6 +35,9 @@ class DecodeParams {
// Resize the image to a smaller size before scanning to improve performance
int maxSize;
// Whether to scan multiple barcodes
bool isMultiScan;
}
// Represents the parameters for encoding a barcode

5
lib/src/models/position.dart

@ -1,6 +1,8 @@
/// Represents the position of a barcode in an image.
class Position {
Position(
this.imageWidth,
this.imageHeight,
this.topLeftX,
this.topLeftY,
this.topRightX,
@ -11,6 +13,9 @@ class Position {
this.bottomRightY,
);
int imageWidth; // width of the image
int imageHeight; // height of the image
int topLeftX; // x coordinate of top left corner of barcode
int topLeftY; // y coordinate of top left corner of barcode
int topRightX; // x coordinate of top right corner of barcode

49
lib/src/ui/reader_widget.dart

@ -11,9 +11,12 @@ import '../../flutter_zxing.dart';
class ReaderWidget extends StatefulWidget {
const ReaderWidget({
super.key,
required this.onScan,
this.onScan,
this.onScanFailure,
this.onMultiScan,
this.onMultiScanFailure,
this.onControllerCreated,
this.isMultiScan = false,
this.codeFormat = Format.any,
this.tryHarder = false,
this.tryInverted = false,
@ -30,14 +33,23 @@ class ReaderWidget extends StatefulWidget {
});
/// Called when a code is detected
final Function(Code) onScan;
final Function(Code)? onScan;
/// Called when a code is not detected
final Function(Code)? onScanFailure;
/// Called when a code is detected
final Function(List<Code>)? onMultiScan;
/// Called when a code is not detected
final Function(List<Code>)? onMultiScanFailure;
/// Called when the camera controller is created
final Function(CameraController?)? onControllerCreated;
/// Allow multiple scans
final bool isMultiScan;
/// Code format to scan
final int codeFormat;
@ -207,17 +219,32 @@ class _ReaderWidgetState extends State<ReaderWidget>
cropHeight: cropSize,
tryHarder: widget.tryHarder,
tryInverted: widget.tryInverted,
isMultiScan: widget.isMultiScan,
);
final Code result = await zx.processCameraImage(
image,
params: params,
);
if (result.isValid) {
widget.onScan(result);
setState(() {});
await Future<void>.delayed(widget.scanDelaySuccess);
if (widget.isMultiScan) {
final List<Code> results = await zx.processCameraImageMulti(
image,
params: params,
);
if (results.isNotEmpty) {
widget.onMultiScan?.call(results);
await Future<void>.delayed(widget.scanDelaySuccess);
setState(() {});
} else {
widget.onMultiScanFailure?.call(results);
}
} else {
widget.onScanFailure?.call(result);
final Code result = await zx.processCameraImage(
image,
params: params,
);
if (result.isValid) {
widget.onScan?.call(result);
setState(() {});
await Future<void>.delayed(widget.scanDelaySuccess);
} else {
widget.onScanFailure?.call(result);
}
}
} on FileSystemException catch (e) {
debugPrint(e.message);

26
lib/src/utils/extentions.dart

@ -8,18 +8,18 @@ import '../../zxing_mobile.dart';
extension CodeExt on CodeResult {
Code toCode() {
return Code(
text == nullptr ? null : text.cast<Utf8>().toDartString(),
isValid == 1,
error == nullptr ? null : error.cast<Utf8>().toDartString(),
bytes == nullptr
? null
: Uint8List.fromList(bytes.cast<Int8>().asTypedList(length)),
format,
pos == nullptr ? null : pos.ref.toPosition(),
isInverted == 1,
isMirrored == 1,
duration,
);
text == nullptr ? null : text.cast<Utf8>().toDartString(),
isValid == 1,
error == nullptr ? null : error.cast<Utf8>().toDartString(),
bytes == nullptr
? null
: Uint8List.fromList(bytes.cast<Int8>().asTypedList(length)),
format,
pos == nullptr ? null : pos.ref.toPosition(),
isInverted == 1,
isMirrored == 1,
duration,
);
}
}
@ -38,6 +38,8 @@ extension EncodeExt on EncodeResult {
extension PoeExt on Pos {
Position toPosition() => Position(
imageWidth,
imageHeight,
topLeftX,
topLeftY,
topRightX,

18
lib/src/utils/isolate_utils.dart

@ -57,14 +57,18 @@ class IsolateUtils {
try {
final CameraImage image = isolateData.cameraImage;
final Uint8List bytes = await convertImage(image);
final int width = image.width;
final int height = image.height;
final DecodeParams params = isolateData.params;
final Code result = zxingReadBarcode(
bytes,
width: image.width,
height: image.height,
params: isolateData.params,
);
dynamic result;
if (params.isMultiScan) {
result = zxingReadBarcodes(bytes,
width: width, height: height, params: params);
} else {
result = zxingReadBarcode(bytes,
width: width, height: height, params: params);
}
isolateData.responsePort?.send(result);
} catch (e) {
isolateData.responsePort?.send(e);

11
lib/zxing_mobile.dart

@ -42,8 +42,15 @@ class ZxingMobile implements Zxing {
Future<Code> processCameraImage(
CameraImage image, {
DecodeParams? params,
}) =>
zxingProcessCameraImage(image, params: params);
}) async =>
await zxingProcessCameraImage(image, params: params) as Code;
@override
Future<List<Code>> processCameraImageMulti(
CameraImage image, {
DecodeParams? params,
}) async =>
await zxingProcessCameraImage(image, params: params) as List<Code>;
@override
Future<Code?> readBarcodeImagePathString(

7
lib/zxing_web.dart

@ -38,6 +38,13 @@ class ZxingWeb implements Zxing {
}) =>
throw UnimplementedError();
@override
Future<List<Code>> processCameraImageMulti(
CameraImage image, {
DecodeParams? params,
}) =>
throw UnimplementedError();
@override
Future<Code?> readBarcodeImagePathString(
String path, {

7
src/native_zxing.cpp

@ -51,6 +51,8 @@ extern "C"
int evalInMillis = static_cast<int>(get_now() - start);
code.duration = evalInMillis;
code.pos->imageWidth = width;
code.pos->imageHeight = height;
platform_log("Read Barcode in: %d ms\n", code.duration);
return code;
}
@ -82,14 +84,13 @@ extern "C"
struct CodeResult code;
resultToCodeResult(&code, result);
code.duration = evalInMillis;
code.pos->imageWidth = width;
code.pos->imageHeight = height;
codes[i] = code;
i++;
}
delete[] data;
delete[] bytes;
return {i, codes};
}

4
src/native_zxing.h

@ -9,6 +9,8 @@ extern "C"
*/
struct Pos
{
int imageWidth; ///< The width of the image
int imageHeight; ///< The height of the image
int topLeftX; ///< x coordinate of top left corner of barcode
int topLeftY; ///< y coordinate of top left corner of barcode
int topRightX; ///< x coordinate of top right corner of barcode
@ -115,7 +117,7 @@ extern "C"
struct EncodeResult encodeBarcode(char *contents, int width, int height, int format, int margin, int eccLevel);
// Private functions
void resultToCodeResult(struct CodeResult *code, ZXing::Result result);
// void resultToCodeResult(struct CodeResult *code, ZXing::Result result);
#ifdef __cplusplus
}

2
zxscanner/pubspec.lock

@ -321,7 +321,7 @@ packages:
name: flutter_zxing
url: "https://pub.dartlang.org"
source: hosted
version: "0.10.0"
version: "1.0.0-beta.1"
font_awesome_flutter:
dependency: "direct main"
description:

2
zxscanner/pubspec.yaml

@ -17,7 +17,7 @@ dependencies:
sdk: flutter
flutter_markdown: ^0.6.13
flutter_mobx: ^2.0.6
flutter_zxing: ^0.10.0
flutter_zxing: 1.0.0-beta.1
font_awesome_flutter: ^10.3.0
hive: ^2.2.3
hive_flutter: ^1.1.0

Loading…
Cancel
Save