Hi,
I am trying to build a flutter plugin that uses ffi to run native C++ with the OpenCV library and I found these instructions (Implementing edge detection in Flutter 🤓) as guidance. After setting everything up and creating a sample app to use the flutter plugin I am getting the following error during the build step of the sample app
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':simple_edge_detection:buildCMakeDebug[arm64-v8a]'.
> com.android.ide.common.process.ProcessException: ninja: Entering directory `/media/vlad/FEF8B6AEF8B66495/Projects/Me/flutter_cpp_opencv_tutorial/simple_edge_detection/android/.cxx/Debug/2xn6wz61/arm64-v8a'
[1/3] Building CXX object CMakeFiles/native_edge_detection.dir/media/vlad/FEF8B6AEF8B66495/Projects/Me/flutter_cpp_opencv_tutorial/simple_edge_detection/ios/Classes/edge_detector.cpp.o
FAILED: CMakeFiles/native_edge_detection.dir/media/vlad/FEF8B6AEF8B66495/Projects/Me/flutter_cpp_opencv_tutorial/simple_edge_detection/ios/Classes/edge_detector.cpp.o
/home/vlad/Android/Sdk/ndk/23.1.7779620/toolchains/llvm/prebuilt/linux-x86_64/bin/clang++ --target=aarch64-none-linux-android21 --gcc-toolchain=/home/vlad/Android/Sdk/ndk/23.1.7779620/toolchains/llvm/prebuilt/linux-x86_64 --sysroot=/home/vlad/Android/Sdk/ndk/23.1.7779620/toolchains/llvm/prebuilt/linux-x86_64/sysroot -Dnative_edge_detection_EXPORTS -I../../../../../include -g -DANDROID -fdata-sections -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -D_FORTIFY_SOURCE=2 -Wformat -Werror=format-security -frtti -fexceptions -std=c++11 -fno-limit-debug-info -fPIC -MD -MT CMakeFiles/native_edge_detection.dir/media/vlad/FEF8B6AEF8B66495/Projects/Me/flutter_cpp_opencv_tutorial/simple_edge_detection/ios/Classes/edge_detector.cpp.o -MF CMakeFiles/native_edge_detection.dir/media/vlad/FEF8B6AEF8B66495/Projects/Me/flutter_cpp_opencv_tutorial/simple_edge_detection/ios/Classes/edge_detector.cpp.o.d -o CMakeFiles/native_edge_detection.dir/media/vlad/FEF8B6AEF8B66495/Projects/Me/flutter_cpp_opencv_tutorial/simple_edge_detection/ios/Classes/edge_detector.cpp.o -c /media/vlad/FEF8B6AEF8B66495/Projects/Me/flutter_cpp_opencv_tutorial/simple_edge_detection/ios/Classes/edge_detector.cpp
In file included from /media/vlad/FEF8B6AEF8B66495/Projects/Me/flutter_cpp_opencv_tutorial/simple_edge_detection/ios/Classes/edge_detector.cpp:1:
In file included from /media/vlad/FEF8B6AEF8B66495/Projects/Me/flutter_cpp_opencv_tutorial/simple_edge_detection/ios/Classes/edge_detector.hpp:1:
In file included from ../../../../../include/opencv2/opencv.hpp:52:
In file included from ../../../../../include/opencv2/core.hpp:54:
In file included from ../../../../../include/opencv2/core/base.hpp:58:
In file included from ../../../../../include/opencv2/core/cvstd.hpp:81:
In file included from ../../../../../include/opencv2/core/cvstd_wrapper.hpp:11:
/snap/flutter/current/usr/include/c++/9/memory:121:25: error: cast from pointer to smaller type 'uintptr_t' (aka 'unsigned int') loses information
const auto __intptr = reinterpret_cast<uintptr_t>(__ptr);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 error generated.
[2/3] Building CXX object CMakeFiles/native_edge_detection.dir/media/vlad/FEF8B6AEF8B66495/Projects/Me/flutter_cpp_opencv_tutorial/simple_edge_detection/ios/Classes/native_edge_detection.cpp.o
FAILED: CMakeFiles/native_edge_detection.dir/media/vlad/FEF8B6AEF8B66495/Projects/Me/flutter_cpp_opencv_tutorial/simple_edge_detection/ios/Classes/native_edge_detection.cpp.o
/home/vlad/Android/Sdk/ndk/23.1.7779620/toolchains/llvm/prebuilt/linux-x86_64/bin/clang++ --target=aarch64-none-linux-android21 --gcc-toolchain=/home/vlad/Android/Sdk/ndk/23.1.7779620/toolchains/llvm/prebuilt/linux-x86_64 --sysroot=/home/vlad/Android/Sdk/ndk/23.1.7779620/toolchains/llvm/prebuilt/linux-x86_64/sysroot -Dnative_edge_detection_EXPORTS -I../../../../../include -g -DANDROID -fdata-sections -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -D_FORTIFY_SOURCE=2 -Wformat -Werror=format-security -frtti -fexceptions -std=c++11 -fno-limit-debug-info -fPIC -MD -MT CMakeFiles/native_edge_detection.dir/media/vlad/FEF8B6AEF8B66495/Projects/Me/flutter_cpp_opencv_tutorial/simple_edge_detection/ios/Classes/native_edge_detection.cpp.o -MF CMakeFiles/native_edge_detection.dir/media/vlad/FEF8B6AEF8B66495/Projects/Me/flutter_cpp_opencv_tutorial/simple_edge_detection/ios/Classes/native_edge_detection.cpp.o.d -o CMakeFiles/native_edge_detection.dir/media/vlad/FEF8B6AEF8B66495/Projects/Me/flutter_cpp_opencv_tutorial/simple_edge_detection/ios/Classes/native_edge_detection.cpp.o -c /media/vlad/FEF8B6AEF8B66495/Projects/Me/flutter_cpp_opencv_tutorial/simple_edge_detection/ios/Classes/native_edge_detection.cpp
In file included from /media/vlad/FEF8B6AEF8B66495/Projects/Me/flutter_cpp_opencv_tutorial/simple_edge_detection/ios/Classes/native_edge_detection.cpp:2:
In file included from /media/vlad/FEF8B6AEF8B66495/Projects/Me/flutter_cpp_opencv_tutorial/simple_edge_detection/ios/Classes/edge_detector.hpp:1:
In file included from ../../../../../include/opencv2/opencv.hpp:52:
In file included from ../../../../../include/opencv2/core.hpp:54:
In file included from ../../../../../include/opencv2/core/base.hpp:58:
In file included from ../../../../../include/opencv2/core/cvstd.hpp:81:
In file included from ../../../../../include/opencv2/core/cvstd_wrapper.hpp:11:
/snap/flutter/current/usr/include/c++/9/memory:121:25: error: cast from pointer to smaller type 'uintptr_t' (aka 'unsigned int') loses information
const auto __intptr = reinterpret_cast<uintptr_t>(__ptr);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 error generated.
ninja: build stopped: subcommand failed.
C++ build system [build] failed while executing:
/home/vlad/Android/Sdk/cmake/3.18.1/bin/ninja \
-C \
/media/vlad/FEF8B6AEF8B66495/Projects/Me/flutter_cpp_opencv_tutorial/simple_edge_detection/android/.cxx/Debug/2xn6wz61/arm64-v8a \
native_edge_detection
from /media/vlad/FEF8B6AEF8B66495/Projects/Me/flutter_cpp_opencv_tutorial/simple_edge_detection/android
* Try:
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.
* Get more help at https://help.gradle.org
BUILD FAILED in 5s
Exception: Gradle task assembleDebug failed with exit code 1
The CMakeLists.txt has the following content
cmake_minimum_required(VERSION 3.16.3)
include_directories(../include)
add_library(lib_opencv SHARED IMPORTED)
set_target_properties(lib_opencv PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libopencv_java4.so)
set(EDGE_DETECTION_DIR "../ios/Classes")
set(SOURCES
${EDGE_DETECTION_DIR}/native_edge_detection.cpp
${EDGE_DETECTION_DIR}/edge_detector.cpp
)
add_library(native_edge_detection SHARED ${SOURCES})
target_link_libraries(native_edge_detection lib_opencv)
The build.gradle file has the following content
group 'dev.flutterclutter.simple_edge_detection'
version '1.0-SNAPSHOT'
buildscript {
ext.kotlin_version = '1.7.10'
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.3.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
allprojects {
repositories {
google()
mavenCentral()
}
}
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
android {
compileSdkVersion 31
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
test.java.srcDirs += 'src/test/kotlin'
}
defaultConfig {
minSdkVersion 16
}
dependencies {
testImplementation 'org.jetbrains.kotlin:kotlin-test'
testImplementation 'org.mockito:mockito-core:5.0.0'
}
testOptions {
unitTests.all {
useJUnitPlatform()
testLogging {
events "passed", "skipped", "failed", "standardOut", "standardError"
outputs.upToDateWhen {false}
showStandardStreams = true
}
}
}
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
defaultConfig {
externalNativeBuild {
cmake {
cppFlags '-frtti -fexceptions -std=c++11'
arguments '-DANDROID_STL=c++_shared'
}
}
}
}
The C++ exposed to ffi is the following
#include "native_edge_detection.hpp"
#include "edge_detector.hpp"
#include <stdlib.h>
#include <opencv2/opencv.hpp>
extern "C" __attribute__((visibility("default"))) __attribute__((used))
struct Coordinate *create_coordinate(double x, double y)
{
struct Coordinate *coordinate = (struct Coordinate *)malloc(sizeof(struct Coordinate));
coordinate->x = x;
coordinate->y = y;
return coordinate;
}
extern "C" __attribute__((visibility("default"))) __attribute__((used))
struct DetectionResult *create_detection_result(Coordinate *topLeft, Coordinate *topRight, Coordinate *bottomLeft, Coordinate *bottomRight)
{
struct DetectionResult *detectionResult = (struct DetectionResult *)malloc(sizeof(struct DetectionResult));
detectionResult->topLeft = topLeft;
detectionResult->topRight = topRight;
detectionResult->bottomLeft = bottomLeft;
detectionResult->bottomRight = bottomRight;
return detectionResult;
}
extern "C" __attribute__((visibility("default"))) __attribute__((used))
struct DetectionResult *detect_edges(char *str) {
struct DetectionResult *coordinate = (struct DetectionResult *)malloc(sizeof(struct DetectionResult));
cv::Mat mat = cv::imread(str);
if (mat.size().width == 0 || mat.size().height == 0) {
return create_detection_result(
create_coordinate(0, 0),
create_coordinate(1, 0),
create_coordinate(0, 1),
create_coordinate(1, 1)
);
}
vector<cv::Point> points = EdgeDetector::detect_edges(mat);
return create_detection_result(
create_coordinate((double)points[0].x / mat.size().width, (double)points[0].y / mat.size().height),
create_coordinate((double)points[1].x / mat.size().width, (double)points[1].y / mat.size().height),
create_coordinate((double)points[2].x / mat.size().width, (double)points[2].y / mat.size().height),
create_coordinate((double)points[3].x / mat.size().width, (double)points[3].y / mat.size().height)
);
}
with the corresponding header file
struct Coordinate
{
double x;
double y;
};
struct DetectionResult
{
Coordinate* topLeft;
Coordinate* topRight;
Coordinate* bottomLeft;
Coordinate* bottomRight;
};
extern "C"
struct DetectionResult *detect_edges(char *str);
The ffi implementation is the following
import 'dart:async';
import 'dart:ffi';
import 'dart:io';
import 'dart:convert' show utf8;
import 'package:ffi/ffi.dart';
import 'package:flutter/material.dart';
base class Coordinate extends Struct {
@Double()
external double x;
@Double()
external double y;
factory Coordinate.allocate(double x, double y) => calloc<Coordinate>().ref
..x = x
..y = y;
}
base class NativeDetectionResult extends Struct {
external Pointer<Coordinate> topLeft;
external Pointer<Coordinate> topRight;
external Pointer<Coordinate> bottomLeft;
external Pointer<Coordinate> bottomRight;
factory NativeDetectionResult.allocate(
Pointer<Coordinate> topLeft,
Pointer<Coordinate> topRight,
Pointer<Coordinate> bottomLeft,
Pointer<Coordinate> bottomRight) =>
calloc<NativeDetectionResult>().ref
..topLeft = topLeft
..topRight = topRight
..bottomLeft = bottomLeft
..bottomRight = bottomRight;
}
class EdgeDetectionResult {
EdgeDetectionResult({
required this.topLeft,
required this.topRight,
required this.bottomLeft,
required this.bottomRight,
});
Offset topLeft;
Offset topRight;
Offset bottomLeft;
Offset bottomRight;
}
typedef DetectEdgesFunction = Pointer<NativeDetectionResult> Function(
Pointer<Utf8> x);
class EdgeDetection {
static Future<EdgeDetectionResult> detectEdges(String path) async {
DynamicLibrary nativeEdgeDetection = _getDynamicLibrary();
final detectEdges = nativeEdgeDetection
.lookup<NativeFunction<DetectEdgesFunction>>("detect_edges")
.asFunction<DetectEdgesFunction>();
NativeDetectionResult detectionResult = detectEdges(utf8.encode(path) as Pointer<Utf8>).ref;
return EdgeDetectionResult(
topLeft: Offset(
detectionResult.topLeft.ref.x, detectionResult.topLeft.ref.y),
topRight: Offset(
detectionResult.topRight.ref.x, detectionResult.topRight.ref.y),
bottomLeft: Offset(
detectionResult.bottomLeft.ref.x, detectionResult.bottomLeft.ref.y),
bottomRight: Offset(detectionResult.bottomRight.ref.x,
detectionResult.bottomRight.ref.y));
}
static DynamicLibrary _getDynamicLibrary() {
final DynamicLibrary nativeEdgeDetection = Platform.isAndroid
? DynamicLibrary.open("libnative_edge_detection.so")
: DynamicLibrary.process();
return nativeEdgeDetection;
}
}
I am not sure how to interpret this error and I could really use your help to get it working. Thanks in advance!