Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: support formats for analyze image #1177

Merged
merged 18 commits into from
Sep 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
## NEXT

* [MacOS] Added the corners and size information to barcode results.
* [MacOS] Added support for `analyzeImage`.
* [web] Added the size information to barcode results.
* Added support for barcode formats to image analysis.

## 5.2.3

Deprecations:
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ See the example app for detailed implementation information.

| Features | Android | iOS | macOS | Web |
|------------------------|--------------------|--------------------|----------------------|-----|
| analyzeImage (Gallery) | :heavy_check_mark: | :heavy_check_mark: | :x: | :x: |
| returnImage | :heavy_check_mark: | :heavy_check_mark: | :x: | :x: |
| analyzeImage (Gallery) | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :x: |
| returnImage | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :x: |
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I forgot to update this one for returnImage

| scanWindow | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :x: |

## Platform Support
Expand Down Expand Up @@ -83,8 +83,8 @@ Ensure that you granted camera permission in XCode -> Signing & Capabilities:

## Web

As of version 5.0.0 adding the library to the `index.html` is no longer required,
as the library is automatically loaded on first use.
As of version 5.0.0 adding the barcode scanning library script to the `index.html` is no longer required,
as the script is automatically loaded on first use.

### Providing a mirror for the barcode scanning library

Expand Down
6 changes: 3 additions & 3 deletions android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ dependencies {
def useUnbundled = project.findProperty('dev.steenbakker.mobile_scanner.useUnbundled') ?: false
if (useUnbundled.toBoolean()) {
// Dynamically downloaded model via Google Play Services
implementation 'com.google.android.gms:play-services-mlkit-barcode-scanning:18.3.0'
implementation 'com.google.android.gms:play-services-mlkit-barcode-scanning:18.3.1'
} else {
// Bundled model in app
implementation 'com.google.mlkit:barcode-scanning:17.2.0'
Expand All @@ -77,8 +77,8 @@ dependencies {
// See: https://youtrack.jetbrains.com/issue/KT-55297/kotlin-stdlib-should-declare-constraints-on-kotlin-stdlib-jdk8-and-kotlin-stdlib-jdk7
implementation(platform("org.jetbrains.kotlin:kotlin-bom:1.8.22"))

implementation 'androidx.camera:camera-lifecycle:1.3.3'
implementation 'androidx.camera:camera-camera2:1.3.3'
implementation 'androidx.camera:camera-lifecycle:1.3.4'
implementation 'androidx.camera:camera-camera2:1.3.4'

testImplementation 'org.jetbrains.kotlin:kotlin-test'
testImplementation 'org.mockito:mockito-core:5.12.0'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,28 +151,16 @@ class MobileScannerHandler(
null
}

var barcodeScannerOptions: BarcodeScannerOptions? = null
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was refactored & moved to reuse it.

if (formats != null) {
val formatsList: MutableList<Int> = mutableListOf()
for (formatValue in formats) {
formatsList.add(BarcodeFormats.fromRawValue(formatValue).intValue)
}
barcodeScannerOptions = if (formatsList.size == 1) {
BarcodeScannerOptions.Builder().setBarcodeFormats(formatsList.first())
.build()
} else {
BarcodeScannerOptions.Builder().setBarcodeFormats(
formatsList.first(),
*formatsList.subList(1, formatsList.size).toIntArray()
).build()
}
}
val barcodeScannerOptions: BarcodeScannerOptions? = buildBarcodeScannerOptions(formats)

val position =
if (facing == 0) CameraSelector.DEFAULT_FRONT_CAMERA else CameraSelector.DEFAULT_BACK_CAMERA

val detectionSpeed: DetectionSpeed = if (speed == 0) DetectionSpeed.NO_DUPLICATES
else if (speed ==1) DetectionSpeed.NORMAL else DetectionSpeed.UNRESTRICTED
val detectionSpeed: DetectionSpeed = when (speed) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was a refactor that was proposed by the Kotlin language tooling

0 -> DetectionSpeed.NO_DUPLICATES
1 -> DetectionSpeed.NORMAL
else -> DetectionSpeed.UNRESTRICTED
}

mobileScanner!!.start(
barcodeScannerOptions,
Expand Down Expand Up @@ -243,13 +231,13 @@ class MobileScannerHandler(

private fun analyzeImage(call: MethodCall, result: MethodChannel.Result) {
analyzerResult = result
val uri = Uri.fromFile(File(call.arguments.toString()))

// TODO: parse options from the method call
// See https://github.com/juliansteenbakker/mobile_scanner/issues/1069
val formats: List<Int>? = call.argument<List<Int>>("formats")
val filePath: String = call.argument<String>("filePath")!!

mobileScanner!!.analyzeImage(
uri,
null,
Uri.fromFile(File(filePath)),
buildBarcodeScannerOptions(formats),
analyzeImageSuccessCallback,
analyzeImageErrorCallback)
}
Expand Down Expand Up @@ -284,4 +272,26 @@ class MobileScannerHandler(

result.success(null)
}

private fun buildBarcodeScannerOptions(formats: List<Int>?): BarcodeScannerOptions? {
if (formats == null) {
return null
}

val formatsList: MutableList<Int> = mutableListOf()

for (formatValue in formats) {
formatsList.add(BarcodeFormats.fromRawValue(formatValue).intValue)
}

if (formatsList.size == 1) {
return BarcodeScannerOptions.Builder().setBarcodeFormats(formatsList.first())
.build()
}

return BarcodeScannerOptions.Builder().setBarcodeFormats(
formatsList.first(),
*formatsList.subList(1, formatsList.size).toIntArray()
).build()
}
}
76 changes: 76 additions & 0 deletions example/lib/barcode_scanner_analyze_image.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:mobile_scanner/mobile_scanner.dart';

class BarcodeScannerAnalyzeImage extends StatefulWidget {
const BarcodeScannerAnalyzeImage({super.key});

@override
State<BarcodeScannerAnalyzeImage> createState() =>
_BarcodeScannerAnalyzeImageState();
}

class _BarcodeScannerAnalyzeImageState
extends State<BarcodeScannerAnalyzeImage> {
final MobileScannerController _controller = MobileScannerController();

BarcodeCapture? _barcodeCapture;

Future<void> _analyzeImageFromFile() async {
try {
final XFile? file =
await ImagePicker().pickImage(source: ImageSource.gallery);

if (!mounted || file == null) {
return;
}

final BarcodeCapture? barcodeCapture =
await _controller.analyzeImage(file.path);

if (mounted) {
setState(() {
_barcodeCapture = barcodeCapture;
});
}
} catch (_) {}
}

@override
Widget build(BuildContext context) {
Widget label = const Text('Pick a file to detect barcode');

if (_barcodeCapture != null) {
label = Text(
_barcodeCapture?.barcodes.firstOrNull?.displayValue ??
'No barcode detected',
);
}

return Scaffold(
appBar: AppBar(title: const Text('Analyze image from file')),
body: Column(
children: [
Expanded(
child: Center(
child: ElevatedButton(
onPressed: kIsWeb ? null : _analyzeImageFromFile,
child: kIsWeb
? const Text('Analyze image is not supported on web')
: const Text('Choose file'),
),
),
),
Expanded(child: Center(child: label)),
],
),
);
}

@override
void dispose() {
_controller.dispose();
super.dispose();
}
}
8 changes: 4 additions & 4 deletions example/lib/barcode_scanner_window.dart
Original file line number Diff line number Diff line change
Expand Up @@ -39,16 +39,16 @@ class _BarcodeScannerWithScanWindowState
final scannedBarcode = barcodeCapture.barcodes.first;

// No barcode corners, or size, or no camera preview size.
if (scannedBarcode.corners.isEmpty ||
value.size.isEmpty ||
barcodeCapture.size.isEmpty) {
if (value.size.isEmpty ||
scannedBarcode.size.isEmpty ||
scannedBarcode.corners.isEmpty) {
return const SizedBox();
}

return CustomPaint(
painter: BarcodeOverlay(
barcodeCorners: scannedBarcode.corners,
barcodeSize: barcodeCapture.size,
barcodeSize: scannedBarcode.size,
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "barcode size" is wrong here

boxFit: BoxFit.contain,
cameraPreviewSize: value.size,
),
Expand Down
133 changes: 57 additions & 76 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:mobile_scanner_example/barcode_scanner_analyze_image.dart';
import 'package:mobile_scanner_example/barcode_scanner_controller.dart';
import 'package:mobile_scanner_example/barcode_scanner_listview.dart';
import 'package:mobile_scanner_example/barcode_scanner_pageview.dart';
Expand All @@ -20,95 +21,75 @@ void main() {
class MyHome extends StatelessWidget {
const MyHome({super.key});

Widget _buildItem(BuildContext context, String label, Widget page) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Center(
child: ElevatedButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => page,
),
);
},
child: Text(label),
),
),
);
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Mobile Scanner Example')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
child: ListView(
children: [
ElevatedButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const BarcodeScannerSimple(),
),
);
},
child: const Text('MobileScanner Simple'),
_buildItem(
context,
'MobileScanner Simple',
const BarcodeScannerSimple(),
),
ElevatedButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const BarcodeScannerListView(),
),
);
},
child: const Text('MobileScanner with ListView'),
_buildItem(
context,
'MobileScanner with ListView',
const BarcodeScannerListView(),
),
ElevatedButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const BarcodeScannerWithController(),
),
);
},
child: const Text('MobileScanner with Controller'),
_buildItem(
context,
'MobileScanner with Controller',
const BarcodeScannerWithController(),
),
ElevatedButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const BarcodeScannerWithScanWindow(),
),
);
},
child: const Text('MobileScanner with ScanWindow'),
_buildItem(
context,
'MobileScanner with ScanWindow',
const BarcodeScannerWithScanWindow(),
),
ElevatedButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const BarcodeScannerReturningImage(),
),
);
},
child: const Text(
'MobileScanner with Controller (returning image)',
),
_buildItem(
context,
'MobileScanner with Controller (return image)',
const BarcodeScannerReturningImage(),
),
_buildItem(
context,
'MobileScanner with zoom slider',
const BarcodeScannerWithZoom(),
),
ElevatedButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const BarcodeScannerWithZoom(),
),
);
},
child: const Text('MobileScanner with zoom slider'),
_buildItem(
context,
'MobileScanner with PageView',
const BarcodeScannerPageView(),
),
ElevatedButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const BarcodeScannerPageView(),
),
);
},
child: const Text('MobileScanner pageView'),
_buildItem(
context,
'MobileScanner with Overlay',
const BarcodeScannerWithOverlay(),
),
ElevatedButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => BarcodeScannerWithOverlay(),
),
);
},
child: const Text('MobileScanner with Overlay'),
_buildItem(
context,
'Analyze image from file',
const BarcodeScannerAnalyzeImage(),
),
],
),
Expand Down
2 changes: 2 additions & 0 deletions example/lib/mobile_scanner_overlay.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import 'package:mobile_scanner_example/scanner_button_widgets.dart';
import 'package:mobile_scanner_example/scanner_error_widget.dart';

class BarcodeScannerWithOverlay extends StatefulWidget {
const BarcodeScannerWithOverlay({super.key});

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