opencv 1.0.2

  • Readme
  • Changelog
  • Example
  • Installing
  • 89

flutter_opencv #

A Flutter plug-in providing a binding to OpenCV-4.x.

Pub Docs flutter-opencv.readthedocs.io Build Status Code Coverage GitHub license

Flutter OpenCV

Usage #

Installation #

In the pubspec.yaml of your flutter project, add the following dependency:

dependencies:
  ...
  opencv: "^1.0.2"

In your library add the following import:

import 'package:opencv/opencv.dart';

Examples #

Core concepts

All functions are currently contained in the ImgProc class. e.g. ImgProc.blur(...)

Variables for integer choices (read pre-defined values/enums for border types, threshold types, etc.) are stored in ImgProc & Core classes, exactly similar to how OpenCV itself does. However, these variables are renamed to suit the lowerCamelCase Dart fashion (just to objectively not lose "health suggestion points" on pub.dev.

i.e. One of the border types is Core.BORDER_REFLECT, can be used in ImgProc.blur(someBytes, someIntArray, someIntArray, Core.borderReflect);

So, if you refer to the OpenCV docs or a tutorial, the function you're implementing in OpenCV is very much identical to that in flutter_opencv.

The only difference is how the data is managed - OpenCV uses the Mat class to store every image as a matrix.

flutter_opencv instead only works on data as an array of bytes.

Basic usage

The only difference in OpenCV's methods and ours - OpenCV functions take values of the source & destination matrices by reference. In Flutter, we use byte arrays instead of Mat() & we do not need to provide a destination byte array.

So, basically, someFunction(sourceMatrix, destinationMatrix, otherValue1, otherValue2, ...) in OpenCV is now instead dynamic destinationMatrix = someFunction(sourceMatrix, otherValue1, otherValue2, ...) in flutter_opencv.

e.g. For simple thresholding, in OpenCV, you'd do Imgproc.threshold(sourceMatrix, destinationMatrix, 80, 255, Imgproc.THRESH_BINARY); Whereas in flutter_opencv you'd do res = await ImgProc.threshold(await file.readAsBytes(), 80, 255, ImgProc.CV_THRESH_BINARY);

Input images & formats

Since we're not representing images as Mat(), we're going to need the byte[] data. I'll write down methods for each (Flutter) Image class here. For now,

  1. Image from File: File file = await DefaultCacheManager().getSingleFile(_URL); will fetch the file from the internet. await file.readAsBytes() can be directly passed to any of the functions, since it returns a byte array.

Storing result, temporarily

dynamic outputMatrix = someFunction(...) will store a byte array in outputMatrix.

Output images & formats

For now,

  1. Image from memory: Image.memory(outputMatrix) will show the image obtained (as a byte array) from the above function.

Cascading functions/effects

Since you're never going to just implement one function, there's a need to be able to cascade. Inputs & outputs to all functions are common - byte arrays. Hence output from one function can directly be fed to the next.

Why not do a full binding? #

I am actually planning to write a full binding and publish it separately later on. I understand that this implementation is lackluster & a lot of functions still need to be added. If you really need something, make a feature request.

Why not use the core OpenCV classes like Mat? #

Because I wanted to provide a simple interface - one that does not require the user to learn OpenCV or it's API. Instead, much of the way the library works is designed around Flutter/Dart, making it easier for Flutter users to pick it up.

Does lesser functions (in comparison to a full binding) mean lower build size? #

Unfortunately, no. The build size would be the same for this, or a fully bound version (which I'll work on after this project reaches certain level of maturity)

Benchmarks? #

None yet. I'm still profiling the plug-in for most use cases, and haven't faced any issues with rendering so far. Will add benchmarks soon. PR/PM me if interested.

What OpenCV version is flutter_opencv built on top of? #

As of now, the plug-in is built on top of the builds provided for OpenCV v4.1. I know that OpenCV v4.3 is out already, but an issue with the android exportable AAR file prevents me from hosting multiple versions right now. In case you need to change your OpenCV version (read downgrade) while working with this library, you can change it the android/build.gradle file. Just change the dependency version to some other supported one for implementation 'com.quickbirdstudios:opencv:4.1.0'.

Please keep in mind that the above build files (AARs) are provided by someone else - https://github.com/quickbirdstudios/opencv-android. I'm grateful to these guys, but unsure of how often (and far) they would maintain the build repositories, so I'm also working on putting multiple different versions of OpenCV builds on bintray - but it's low on the priority list for now.

Getting started #

With Flutter OpenCV #

See the example directory for a complete sample app using Flutter OpenCV.

With Flutter #

For help getting started with Flutter, view the online documentation.

Notes #

  • iOS not supported currently - I do not own a Mac, so deploying for iOS will be a little slow.

Todos #

  • [x] Setup CI for docs
  • [ ] Document using Sphinx, store in /docs
  • [ ] List future features in the doc
  • [ ] Create an issue template
  • [x] Document basic usage
  • [x] Setup CI for build quality/code cov
  • [ ] Document/blog about advanced usage
  • [ ] Create a feature request template
  • [ ] Write tests
  • [ ] Integrate iOS, setup projects/kanban board for iOS development
  • [ ] Build multiple OpenCV versions, host on bintray

Changelog #

Please see the Changelog page to know what's recently changed.

Contributions #

Feel free to contribute to this project.

If you find a bug or want a feature, but don't know how to fix/implement it, please fill an issue.
If you fixed a bug or implemented a feature, please send a pull request.

1.0.2 #

  • All variables confirmed to lowerCamelCase format in ImgProc & Core.
  • Added HoughCircles as an enchancement feature based on Issue #1.
  • Fixed function names; confirmed to lowerCamelCase
  • Fixed pyrUp, pyrDown, adaptiveThreshold functions

1.0.1 #

  • Fixed initial release bugs/readme bugs.

1.0.0 #

  • Initial release - base functions written & documented for.

example/lib/main.dart

import 'dart:io';

import 'package:flutter/material.dart';
import 'dart:async';

import 'package:flutter/services.dart';
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import 'package:opencv/opencv.dart';
import 'package:opencv/core/core.dart';

void main() => runApp(MyApp());

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  String _platformVersion = 'Unknown';
  dynamic res;
  Image image = Image.asset('assets/temp.png');
  Image imageNew = Image.asset('assets/temp.png');
  File file;
  bool preloaded = false;
  bool loaded = false;
  String url =
      "https://i.pinimg.com/564x/54/e2/ae/54e2aeefa75d031813ec56f6b3efc9ad.jpg";
  String dropdownValue = 'None';

  @override
  void initState() {
    super.initState();
    loadImage();
    initPlatformState();
  }

  Future loadImage() async {
    file = await DefaultCacheManager().getSingleFile(url);
    setState(() {
      image = Image.file(file);
      preloaded = true;
    });
  }

  // Platform messages are asynchronous, so we initialize in an async method.
  Future<void> initPlatformState() async {
    String platformVersion;
    // Platform messages may fail, so we use a try/catch PlatformException.
    try {
      platformVersion = await OpenCV.platformVersion;
    } on PlatformException {
      platformVersion = 'Failed to get platform version.';
    }

    // If the widget was removed from the tree while the asynchronous platform
    // message was in flight, we want to discard the reply rather than calling
    // setState to update our non-existent appearance.
    if (!mounted) return;

    setState(() {
      _platformVersion = platformVersion;
    });
  }

  // Platform messages are asynchronous, so we initialize in an async method.
  Future<void> runAFunction(String functionName) async {
    String platformVersion;
    // Platform messages may fail, so we use a try/catch PlatformException.
    try {
      switch (functionName) {
        case 'blur':
          res = await ImgProc.blur(
              await file.readAsBytes(), [45, 45], [20, 30], Core.borderReflect);
          break;
        case 'GaussianBlur':
          res =
              await ImgProc.gaussianBlur(await file.readAsBytes(), [45, 45], 0);
          break;
        case 'medianBlur':
          res = await ImgProc.medianBlur(await file.readAsBytes(), 45);
          break;
        case 'bilateralFilter':
          res = await ImgProc.bilateralFilter(
              await file.readAsBytes(), 15, 80, 80, Core.borderConstant);
          break;
        case 'boxFilter':
          res = await ImgProc.boxFilter(await file.readAsBytes(), 50, [45, 45],
              [-1, -1], true, Core.borderConstant);
          break;
        case 'sqrBoxFilter':
          res =
              await ImgProc.sqrBoxFilter(await file.readAsBytes(), -1, [1, 1]);
          break;
        case 'filter2D':
          res = await ImgProc.filter2D(await file.readAsBytes(), -1, [2, 2]);
          break;
        case 'dilate':
          res = await ImgProc.dilate(await file.readAsBytes(), [2, 2]);
          break;
        case 'erode':
          res = await ImgProc.erode(await file.readAsBytes(), [2, 2]);
          break;
        case 'morphologyEx':
          res = await ImgProc.morphologyEx(
              await file.readAsBytes(), ImgProc.morphTopHat, [5, 5]);
          break;
        case 'pyrUp':
          res = await ImgProc.pyrUp(
              await file.readAsBytes(), [563 * 2, 375 * 2], Core.borderDefault);
          break;
        case 'pyrDown':
          res = await ImgProc.pyrDown(await file.readAsBytes(),
              [563 ~/ 2.toInt(), 375 ~/ 2.toInt()], Core.borderDefault);
          break;
        case 'pyrMeanShiftFiltering':
          res = await ImgProc.pyrMeanShiftFiltering(
              await file.readAsBytes(), 10, 15);
          break;
        case 'threshold':
          res = await ImgProc.threshold(
              await file.readAsBytes(), 80, 255, ImgProc.threshBinary);
          break;
        case 'adaptiveThreshold':
          res = await ImgProc.adaptiveThreshold(await file.readAsBytes(), 125,
              ImgProc.adaptiveThreshMeanC, ImgProc.threshBinary, 11, 12);
          break;
        case 'copyMakeBorder':
          res = await ImgProc.copyMakeBorder(
              await file.readAsBytes(), 20, 20, 20, 20, Core.borderConstant);
          break;
        case 'sobel':
          res = await ImgProc.sobel(await file.readAsBytes(), -1, 1, 1);
          break;
        case 'scharr':
          res = await ImgProc.scharr(
              await file.readAsBytes(), ImgProc.cvSCHARR, 0, 1);
          break;
        case 'laplacian':
          res = await ImgProc.laplacian(await file.readAsBytes(), 10);
          break;
        case 'distanceTransform':
          res = await ImgProc.threshold(
              await file.readAsBytes(), 80, 255, ImgProc.threshBinary);
          res = await ImgProc.distanceTransform(await res, ImgProc.distC, 3);
          break;
        case 'resize':
          res = await ImgProc.resize(
              await file.readAsBytes(), [500, 500], 0, 0, ImgProc.interArea);
          break;
        case 'applyColorMap':
          res = await ImgProc.applyColorMap(
              await file.readAsBytes(), ImgProc.colorMapHot);
          break;
        case 'houghCircles':
          res = await ImgProc.cvtColor(await file.readAsBytes(), 6);
          res = await ImgProc.houghCircles(
              await res, 3, 2.1, 0.1, 150, 100, 0, 0);
          break;
        default:
          print("No function selected");
          break;
      }

      setState(() {
        imageNew = Image.memory(res);
        loaded = true;
      });
    } on PlatformException {
      platformVersion = 'Failed to get platform version.';
    }

    if (!mounted) return;
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
          appBar: AppBar(
            title: const Text('Plugin example app'),
          ),
          body: Column(
            children: <Widget>[
              Center(
                child: Text('Running on: $_platformVersion\n'),
              ),
              Text("Before"),
              preloaded
                  ? image
                  : Text("There might be an error in loading your asset."),
              Row(
                mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                children: <Widget>[
                  DropdownButton<String>(
                    value: dropdownValue,
                    icon: Icon(Icons.arrow_downward),
                    iconSize: 24,
                    elevation: 16,
                    underline: Container(
                      color: Colors.grey,
                      height: 2,
                    ),
                    onChanged: (String newValue) {
                      setState(() {
                        dropdownValue = newValue;
                      });
                    },
                    items: <String>[
                      'None',
                      'blur',
                      'GaussianBlur',
                      'medianBlur',
                      'bilateralFilter',
                      'boxFilter',
                      'sqrBoxFilter',
                      'filter2D',
                      'threshold',
                      'dilate',
                      'erode',
                      'morphologyEx',
                      'pyrUp',
                      'pyrDown',
                      'pyrMeanShiftFiltering',
                      'threshold',
                      'adaptiveThreshold',
                      'copyMakeBorder',
                      'sobel',
                      'scharr',
                      'laplacian',
                      'distanceTransform',
                      'resize',
                      'applyColorMap',
                      'houghCircles',
                    ].map<DropdownMenuItem<String>>((String value) {
                      return DropdownMenuItem<String>(
                        value: value,
                        child: Text(value),
                      );
                    }).toList(),
                  ),
                  RaisedButton(
                    onPressed: () {
                      runAFunction(dropdownValue);
                    },
                    child: Text('Run'),
                  ),
                ],
              ),
              Text("After"),
              loaded ? imageNew : Container()
            ],
          )),
    );
  }
}

Use this package as a library

1. Depend on it

Add this to your package's pubspec.yaml file:


dependencies:
  opencv: ^1.0.2

2. Install it

You can install packages from the command line:

with Flutter:


$ flutter pub get

Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:


import 'package:opencv/opencv.dart';
  
Popularity:
Describes how popular the package is relative to other packages. [more]
79
Health:
Code health derived from static analysis. [more]
100
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
100
Overall:
Weighted score of the above. [more]
89
Learn more about scoring.

We analyzed this package on Jul 10, 2020, and provided a score, details, and suggestions below. Analysis was completed with status completed using:

  • Dart: 2.8.4
  • pana: 0.13.14
  • Flutter: 1.17.5

Analysis suggestions

Package does not support Flutter platform linux

Because:

  • package:opencv/opencv.dart that declares support for platforms: android, ios

Package does not support Flutter platform macos

Because:

  • package:opencv/opencv.dart that declares support for platforms: android, ios

Package does not support Flutter platform web

Because:

  • package:opencv/opencv.dart that declares support for platforms: android, ios

Package does not support Flutter platform windows

Because:

  • package:opencv/opencv.dart that declares support for platforms: android, ios

Package not compatible with SDK dart

Because:

  • opencv that is a package requiring null.

Health issues and suggestions

Document public APIs. (-0.11 points)

476 out of 491 API elements have no dartdoc comment.Providing good documentation for libraries, classes, functions, and other API elements improves code readability and helps developers find and use your API.

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=2.1.0 <3.0.0
flutter 0.0.0
Transitive dependencies
collection 1.14.12 1.14.13
meta 1.1.8 1.2.2
sky_engine 0.0.99
typed_data 1.1.6 1.2.0
vector_math 2.0.8 2.1.0-nullsafety
Dev dependencies
flutter_test