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

Adds mediapipe_text package #12

Merged
merged 41 commits into from
Mar 11, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
2b45512
adds mediapipe_text package
craiglabenz Oct 13, 2023
7b20bfb
Update .vscode/settings.json
craiglabenz Nov 13, 2023
1de6617
resync headers
craiglabenz Nov 13, 2023
fed1f14
regenerated core bindings
craiglabenz Nov 13, 2023
4cd5730
Apply suggestions from code review
Piinks Nov 15, 2023
37b2c76
Adds example to `mediapipe-task-text` (#15)
craiglabenz Jan 22, 2024
967bea9
added base Dart class ClassificationResult to consolidate results log…
craiglabenz Jan 22, 2024
d5113e1
Update Makefile
Piinks Jan 23, 2024
e03ede5
Update Makefile
Piinks Jan 23, 2024
826f0d2
Update packages/mediapipe-task-text/build.dart
craiglabenz Feb 13, 2024
1bdbb1e
Update packages/mediapipe-task-text/lib/src/tasks/text_classification…
craiglabenz Feb 13, 2024
5f13c18
code review responses
craiglabenz Feb 13, 2024
5f87e76
formatting
craiglabenz Feb 13, 2024
1bc407c
removes stale comment
craiglabenz Feb 13, 2024
070147d
adds Dart to Native converters, with tests
craiglabenz Feb 13, 2024
34a824e
changes from code review
craiglabenz Feb 14, 2024
9887fee
updates mediapipe-core to prepare for IO/web split
craiglabenz Feb 26, 2024
caf3413
Improves memory management in core tests
craiglabenz Feb 26, 2024
fd27076
updated ffigen / bindings
craiglabenz Feb 29, 2024
9029f18
refactors text package for better memory management and eventual web/…
craiglabenz Feb 29, 2024
fcfc59f
removed stale test
craiglabenz Feb 29, 2024
163b213
cleanup on aisle COMMENTS
craiglabenz Feb 29, 2024
7a865b4
Comments and documentation improvements
craiglabenz Mar 1, 2024
8403b9b
Moved log statement
craiglabenz Mar 5, 2024
478f9b1
Removed native memory management helpers in favor of `free` extension…
craiglabenz Mar 7, 2024
24ddcc8
Renamed abstract classes to have Base prefix
craiglabenz Mar 7, 2024
1e60ba8
sorted out class constructors
craiglabenz Mar 8, 2024
2e99712
Improved docstrings explaining memory ownership
craiglabenz Mar 8, 2024
c62bedf
Convert lists to lazy iterable / generators
craiglabenz Mar 8, 2024
4aab937
added missing licenses
craiglabenz Mar 8, 2024
9a43aac
Update packages/mediapipe-task-text/example/test/widgets_test.dart
craiglabenz Mar 8, 2024
5b5e400
Update packages/mediapipe-task-text/example/lib/main.dart
craiglabenz Mar 8, 2024
a576c39
completed return style change
craiglabenz Mar 8, 2024
93fe9c6
CI troubleshooting
craiglabenz Mar 8, 2024
680160d
moved around debugging code
craiglabenz Mar 8, 2024
f7d6d0f
logging tweak
craiglabenz Mar 11, 2024
f7f6490
cat native-assets.yaml
craiglabenz Mar 11, 2024
f3594cb
moar logs
craiglabenz Mar 11, 2024
db148ab
removed bad echo
craiglabenz Mar 11, 2024
b07e023
fixed native-assets typo
craiglabenz Mar 11, 2024
f0aaa5c
Removes CI debugging statements
craiglabenz Mar 11, 2024
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
6 changes: 4 additions & 2 deletions .github/workflows/main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ on:
push:
branches: [main]
pull_request:
branches: [main, ffi-wrapper, ffi-wrapper-core-pkg]
branches: [main, ffi-wrapper, ffi-wrapper-text-pkg]
workflow_dispatch:
schedule:
- cron: "0 0 * * *" # Every day at midnight
Expand All @@ -25,7 +25,9 @@ jobs:
strategy:
fail-fast: false
matrix:
flutter_version: [stable, beta, master]
flutter_version: [master]
# TODO(craiglabenz): reverse once CI works
# flutter_version: [stable, beta, master]
# TODO(craiglabenz): Add `ubuntu-latest` and `windows-latest` when those artifacts exist
os: [macos-latest]
steps:
Expand Down
8 changes: 8 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"files.associations": {
"base_options.h": "c",
"__config": "c",
"text_classifier.h": "c",
"category.h": "c"
}
}
39 changes: 23 additions & 16 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,16 +1,30 @@
# Runs the utility to pull in all header files from `google/mediapipe`
headers:
cd packages/build_cmd && dart bin/main.dart headers
cd tool/builder && dart bin/main.dart headers

# Runs `ffigen` for all packages
generate: generate_core generate_text

# Runs `ffigen` for all packages, compiles the faked C artifacts, and runs all tests
# Runs `ffigen` for all packages and runs all tests
test: generate_text test_text generate_core test_core

# Runs `ffigen` for all packages and all tests for all packages
# Runs all tests for all packages
test_only: test_core test_text

# Runs `sdks_finder` to update manifest files
sdks:
dart tool/builder/bin/main.dart sdks

# Rebuilds the MediaPipe task for macOS
# Assumes google/mediapipe and google/flutter-mediapipe are siblings on the file system
compile_text_classifier_macos_arm:
cd ../mediapipe && bazel build --linkopt -s --config darwin_arm64 --strip always --define MEDIAPIPE_DISABLE_GPU=1 mediapipe/tasks/c/text/text_classifier:libtext_classifier.dylib
cd ../mediapipe && sudo cp bazel-bin/mediapipe/tasks/c/text/text_classifier/libtext_classifier.dylib ../flutter-mediapipe/packages/mediapipe-task-text/example/assets/libtext_classifier_arm64.dylib

compile_text_classifier_macos_x86:
cd ../mediapipe && bazel build --linkopt -s --config darwin_x86_64 --strip always --define MEDIAPIPE_DISABLE_GPU=1 mediapipe/tasks/c/text/text_classifier:libtext_classifier.dylib
cd ../mediapipe && sudo cp bazel-bin/mediapipe/tasks/c/text/text_classifier/libtext_classifier.dylib ../flutter-mediapipe/packages/mediapipe-task-text/example/assets/libtext_classifier_x64.dylib

# Runs `sdks_finder` to update manifest files
sdks:
dart tool/builder/bin/main.dart sdks
Expand All @@ -29,19 +43,12 @@ test_core:

# Runs `ffigen` for `mediapipe_text`
generate_text:
cd packages/mediapipe-task-text && dart run ffigen --config=ffigen.yaml

# Compiles the faked C artifacts for testing
compile_fake_text:
# Builds standalone executable
cd packages/mediapipe-task-text/test/c && gcc fake_text_classifier.c -o fake_text_classifier
# Builds what Flutter needs
cd packages/mediapipe-task-text/test/c && gcc -static -c -fPIC *.c -o fake_text_classifier.o
cd packages/mediapipe-task-text/test/c && gcc -shared -o fake_text_classifier.dylib fake_text_classifier.o

# Runs `ffigen` for `mediapipe_text` and all text tests
test_text: compile_fake_text test_text_only
cd packages/mediapipe-task-text && dart --enable-experiment=native-assets run ffigen --config=ffigen.yaml

# Runs all text tests
test_text_only:
test_text:
cd packages/mediapipe-task-text && flutter test
cd packages/mediapipe-task-text/example && flutter test

example_text:
cd packages/mediapipe-task-text/example && flutter run -d macos
Piinks marked this conversation as resolved.
Show resolved Hide resolved
craiglabenz marked this conversation as resolved.
Show resolved Hide resolved
2 changes: 1 addition & 1 deletion packages/mediapipe-core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,4 @@ Issues that are specific to Flutter can be filed in the [Flutter issue tracker](

To contribute a change to this plugin,
please review our [contribution guide](https://github.com/google/flutter-mediapipe/blob/master/CONTRIBUTING.md)
and open a [pull request](https://github.com/google/flutter-mediapipe/pulls).
and open a [pull request](https://github.com/google/flutter-mediapipe/pulls).
4 changes: 4 additions & 0 deletions packages/mediapipe-core/analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
include: ../analysis_options.yaml

linter:
rules:
- public_member_api_docs # see https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#documentation-dartdocs-javadocs-etc
craiglabenz marked this conversation as resolved.
Show resolved Hide resolved

analyzer:
exclude:
- "**/mediapipe_common_bindings.dart"
2 changes: 1 addition & 1 deletion packages/mediapipe-core/ffigen.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ output:
import-path: "package:mediapipe_core/src/third_party/mediapipe/generated/mediapipe_common_bindings.dart"
headers:
entry-points:
- "third_party/mediapipe/tasks/c/**"
- "third_party/mediapipe/tasks/c/**.h"
preamble: |
/* Copyright 2023 The MediaPipe Authors.

Expand Down
2 changes: 2 additions & 0 deletions packages/mediapipe-core/lib/generated/core_symbols.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ files:
symbols:
c:@S@BaseOptions:
name: BaseOptions
c:@S@Categories:
name: Categories
c:@S@Category:
name: Category
c:@S@ClassificationResult:
Expand Down
7 changes: 6 additions & 1 deletion packages/mediapipe-core/lib/mediapipe_core.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@
/// audio-based tasks.
library;

export 'src/containers.dart' show Category, Classifications;
export 'src/containers.dart'
show
Category,
Classifications,
ClassifierResult,
TimestampedClassifierResult;
export 'src/ffi_utils.dart';
export 'src/task_options.dart' show BaseOptions, ClassifierOptions;
77 changes: 66 additions & 11 deletions packages/mediapipe-core/lib/src/containers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -40,19 +40,19 @@ class Category {

/// Accepts a pointer to a list of structs, and a count representing the length
/// of the list, and returns a list of pure-Dart [Category] instances.
static List<Category> fromStructs(
static List<Category> structsToDart(
Pointer<bindings.Category> structs,
int count,
) {
final categories = <Category>[];
for (int i = 0; i < count; i++) {
categories.add(fromStruct(structs[i]));
categories.add(structToDart(structs[i]));
}
return categories;
}

/// Accepts a pointer to a single struct and returns a pure-Dart [Category] instance.
static Category fromStruct(bindings.Category struct) {
static Category structToDart(bindings.Category struct) {
return Category(
index: struct.index,
score: struct.score,
Expand All @@ -62,19 +62,25 @@ class Category {
}

/// Releases all C memory associated with a list of [bindings.Category] pointers.
/// This method is important to call after calling [Category.fromStructs] to
/// This method is important to call after calling [Category.structsToDart] to
/// convert that C memory into pure-Dart objects.
static void freeStructs(Pointer<bindings.Category> structs, int count) {
int index = 0;
while (index < count) {
bindings.Category obj = structs[index];
calloc.free(obj.category_name);
calloc.free(obj.display_name);
Category.freeStruct(obj);
index++;
}
calloc.free(structs);
}

/// Releases the string fields associated with a single [bindings.Category]
/// struct.
static void freeStruct(bindings.Category struct) {
calloc.free(struct.category_name);
calloc.free(struct.display_name);
}

@override
String toString() => 'Category(index=$index, score=$score, '
'categoryName=$categoryName, displayName=$displayName)';
Expand Down Expand Up @@ -108,22 +114,22 @@ class Classifications {

/// Accepts a pointer to a list of structs, and a count representing the length
/// of the list, and returns a list of pure-Dart [Classifications] instances.
static List<Classifications> fromStructs(
static List<Classifications> structsToDart(
Pointer<bindings.Classifications> structs,
int count,
) {
final classifications = <Classifications>[];
for (int i = 0; i < count; i++) {
classifications.add(fromStruct(structs[i]));
classifications.add(structToDart(structs[i]));
}
return classifications;
}

/// Accepts a pointer to a single struct and returns a pure-Dart [Classifications]
/// instance.
static Classifications fromStruct(bindings.Classifications struct) {
static Classifications structToDart(bindings.Classifications struct) {
return Classifications(
categories: Category.fromStructs(
categories: Category.structsToDart(
struct.categories,
struct.categories_count,
),
Expand All @@ -133,7 +139,7 @@ class Classifications {
}

/// Releases all C memory associated with a list of [bindings.Classifications]
/// pointers. This method is important to call after calling [Classifications.fromStructs]
/// pointers. This method is important to call after calling [Classifications.structsToDart]
/// to convert that C memory into pure-Dart objects.
static void freeStructs(
Pointer<bindings.Classifications> structs,
Expand All @@ -160,3 +166,52 @@ class Classifications {
'headIndex=$headIndex, headName=$headName)';
}
}

/// Container for classification results across any MediaPipe task.
abstract class ClassifierResult {
/// Container for classification results across any MediaPipe task.
const ClassifierResult({
required this.classifications,
});

/// The classification results for each head of the model.
final List<Classifications> classifications;

/// Convenience helper for the first [Classifications] object.
Classifications? get firstClassification =>
classifications.isNotEmpty ? classifications.first : null;

@override
String toString() {
final classificationStrings =
classifications.map((cat) => cat.toString()).join(', ');
return '$runtimeType(classifications=[$classificationStrings])';
}
}

/// Container for classification results that may describe a slice of time
/// within a larger, streaming data source (.e.g, a video or audio file).
abstract class TimestampedClassifierResult extends ClassifierResult {
/// Generative constructor.
const TimestampedClassifierResult({
required super.classifications,
this.timestamp,
});

/// The optional timestamp (as a [Duration]) of the start of the chunk of data
/// corresponding to these results.
///
/// This is only used for classification on time series (e.g. audio
/// classification). In these use cases, the amount of data to process might
/// exceed the maximum size that the model can process: to solve this, the
/// input data is split into multiple chunks starting at different timestamps.
final Duration? timestamp;

@override
String toString() {
final classificationStrings =
classifications.map((cat) => cat.toString()).join(', ');
return '$runtimeType(classifications=[$classificationStrings], '
'timestamp=$timestamp)';
}
}
18 changes: 18 additions & 0 deletions packages/mediapipe-core/lib/src/ffi_utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,21 @@ int _length(Pointer<Uint8> codeUnits) {
}
return length;
}

/// Offers convenience and readability extensions for detecting null pointers.
extension NullAwarePtr on Pointer {
/// Returns true if this is a null pointer.
bool get isNullPointer => address == 0;

/// Returns true if this is not a null pointer.
bool get isNotNullPointer => address != 0;
}

/// Returns true if this nullable pointer is both not-null and not a `NullPtr`,
/// which is to say, its address is 0x00000000.
bool isNotNullOrNullPointer(Pointer? ptr) =>
ptr != null && ptr.isNotNullPointer;

/// Returns true if this nullable pointer is either null OR is a `NullPtr`,
/// which is to say, its address is 0x00000000.
bool isNullOrNullPointer(Pointer? ptr) => ptr == null || ptr.isNullPointer;
craiglabenz marked this conversation as resolved.
Show resolved Hide resolved
Loading