Skip to content

Commit 7c043b7

Browse files
author
Flutterclutter
committed
Implement image cropping and adaptive threshold
1 parent a01bea5 commit 7c043b7

11 files changed

+255
-24
lines changed

.gitignore

+3-1
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,6 @@ example/.idea
1111

1212
*.iml
1313

14-
.idea
14+
.idea
15+
16+
android/.cxx

android/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ set(EDGE_DETECTION_DIR "../ios/Classes")
66
set(SOURCES
77
${EDGE_DETECTION_DIR}/native_edge_detection.cpp
88
${EDGE_DETECTION_DIR}/edge_detector.cpp
9+
${EDGE_DETECTION_DIR}/image_processor.cpp
910
)
1011
add_library(native_edge_detection SHARED ${SOURCES})
1112
target_link_libraries(native_edge_detection lib_opencv)
File renamed without changes.

example/lib/edge_detector.dart

+52-14
Original file line numberDiff line numberDiff line change
@@ -9,35 +9,61 @@ class EdgeDetector {
99
edgeDetectionInput.sendPort.send(result);
1010
}
1111

12+
static Future<void> processImageIsolate(ProcessImageInput processImageInput) async {
13+
EdgeDetection.processImage(processImageInput.inputPath, processImageInput.edgeDetectionResult);
14+
processImageInput.sendPort.send(true);
15+
}
16+
1217
Future<EdgeDetectionResult> detectEdges(String filePath) async {
13-
// Creating a port for communication with isolate and arguments for entry point
1418
final port = ReceivePort();
15-
//final args = ProcessImageArguments(image.path, tempPath);
1619

17-
// Spawning an isolate
18-
Isolate.spawn<EdgeDetectionInput>(
19-
startEdgeDetectionIsolate,
20-
EdgeDetectionInput(
20+
_spawnIsolate<EdgeDetectionInput>(
21+
startEdgeDetectionIsolate,
22+
EdgeDetectionInput(
23+
inputPath: filePath,
24+
sendPort: port.sendPort
25+
),
26+
port
27+
);
28+
29+
return await _subscribeToPort<EdgeDetectionResult>(port);
30+
}
31+
32+
Future<bool> processImage(String filePath, EdgeDetectionResult edgeDetectionResult) async {
33+
final port = ReceivePort();
34+
35+
_spawnIsolate<ProcessImageInput>(
36+
processImageIsolate,
37+
ProcessImageInput(
2138
inputPath: filePath,
39+
edgeDetectionResult: edgeDetectionResult,
2240
sendPort: port.sendPort
2341
),
42+
port
43+
);
44+
45+
return await _subscribeToPort<bool>(port);
46+
}
47+
48+
void _spawnIsolate<T>(Function function, dynamic input, ReceivePort port) {
49+
Isolate.spawn<T>(
50+
function,
51+
input,
2452
onError: port.sendPort,
2553
onExit: port.sendPort
2654
);
55+
}
2756

28-
// Making a variable to store a subscription in
57+
Future<T> _subscribeToPort<T>(ReceivePort port) async {
2958
StreamSubscription sub;
30-
31-
// Listening for messages on port
32-
33-
var completer = new Completer<EdgeDetectionResult>();
34-
59+
60+
var completer = new Completer<T>();
61+
3562
sub = port.listen((result) async {
36-
// Cancel a subscription after message received called
3763
await sub?.cancel();
3864
completer.complete(await result);
3965
});
40-
66+
4167
return completer.future;
4268
}
4369
}
@@ -50,4 +76,16 @@ class EdgeDetectionInput {
5076

5177
String inputPath;
5278
SendPort sendPort;
79+
}
80+
81+
class ProcessImageInput {
82+
ProcessImageInput({
83+
this.inputPath,
84+
this.edgeDetectionResult,
85+
this.sendPort
86+
});
87+
88+
String inputPath;
89+
EdgeDetectionResult edgeDetectionResult;
90+
SendPort sendPort;
5391
}

example/lib/image_view.dart

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import 'dart:io';
2+
import 'package:flutter/material.dart';
3+
4+
class ImageView extends StatefulWidget {
5+
ImageView({
6+
this.imagePath
7+
});
8+
9+
final String imagePath;
10+
11+
@override
12+
_ImageViewState createState() => _ImageViewState();
13+
}
14+
15+
class _ImageViewState extends State<ImageView> {
16+
GlobalKey imageWidgetKey = GlobalKey();
17+
18+
@override
19+
Widget build(BuildContext mainContext) {
20+
return Center(child: Image.file(
21+
File(widget.imagePath),
22+
fit: BoxFit.contain
23+
));
24+
}
25+
}

example/lib/scan.dart

+36-6
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@ import 'package:simple_edge_detection/edge_detection.dart';
77
import 'package:flutter/material.dart';
88
import 'package:image_picker/image_picker.dart';
99
import 'package:path_provider/path_provider.dart';
10-
import 'package:simple_edge_detection_example/image_preview.dart';
10+
import 'package:simple_edge_detection_example/cropping_preview.dart';
1111

1212
import 'camera_view.dart';
1313
import 'edge_detector.dart';
14+
import 'image_view.dart';
1415

1516
class Scan extends StatefulWidget {
1617
@override
@@ -21,6 +22,7 @@ class _ScanState extends State<Scan> {
2122
CameraController controller;
2223
List<CameraDescription> cameras;
2324
String imagePath;
25+
String croppedImagePath;
2426
EdgeDetectionResult edgeDetectionResult;
2527

2628
@override
@@ -44,6 +46,10 @@ class _ScanState extends State<Scan> {
4446
}
4547

4648
Widget _getMainWidget() {
49+
if (croppedImagePath != null) {
50+
return ImageView(imagePath: croppedImagePath);
51+
}
52+
4753
if (imagePath == null && edgeDetectionResult == null) {
4854
return CameraView(
4955
controller: controller
@@ -91,12 +97,18 @@ class _ScanState extends State<Scan> {
9197
return Align(
9298
alignment: Alignment.bottomCenter,
9399
child: FloatingActionButton(
94-
//foregroundColor: Colors.white,
95-
child: Icon(Icons.arrow_back),
100+
child: Icon(Icons.check),
96101
onPressed: () {
102+
if (croppedImagePath == null) {
103+
return _processImage(
104+
imagePath, edgeDetectionResult
105+
);
106+
}
107+
97108
setState(() {
98-
edgeDetectionResult = null;
99109
imagePath = null;
110+
edgeDetectionResult = null;
111+
croppedImagePath = null;
100112
});
101113
},
102114
),
@@ -163,6 +175,24 @@ class _ScanState extends State<Scan> {
163175
});
164176
}
165177

178+
Future _processImage(String filePath, EdgeDetectionResult edgeDetectionResult) async {
179+
if (!mounted || filePath == null) {
180+
return;
181+
}
182+
183+
bool result = await EdgeDetector().processImage(filePath, edgeDetectionResult);
184+
185+
if (result == false) {
186+
return;
187+
}
188+
189+
setState(() {
190+
imageCache.clearLiveImages();
191+
imageCache.clear();
192+
croppedImagePath = imagePath;
193+
});
194+
}
195+
166196
void onTakePictureButtonPressed() async {
167197
String filePath = await takePicture();
168198

@@ -172,8 +202,8 @@ class _ScanState extends State<Scan> {
172202
}
173203

174204
void _onGalleryButtonPressed() async {
175-
final picker = ImagePicker();
176-
final pickedFile = await picker.getImage(source: ImageSource.gallery);
205+
ImagePicker picker = ImagePicker();
206+
PickedFile pickedFile = await picker.getImage(source: ImageSource.gallery);
177207
final filePath = pickedFile.path;
178208

179209
log('Picture saved to $filePath');

ios/Classes/image_processor.cpp

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#include "image_processor.hpp"
2+
#include <opencv2/opencv.hpp>
3+
4+
using namespace cv;
5+
6+
Point2f computePoint(int p1, int p2) {
7+
Point2f pt;
8+
pt.x = p1;
9+
pt.y = p2;
10+
return pt;
11+
}
12+
13+
Mat ImageProcessor::process_image(Mat img, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4) {
14+
cvtColor(img, img, COLOR_BGR2GRAY);
15+
Mat dst = ImageProcessor::crop_and_transform(img, x1, y1, x2, y2, x3, y3, x4, y4);
16+
adaptiveThreshold(dst, dst, 255, cv::ADAPTIVE_THRESH_MEAN_C, cv::THRESH_BINARY, 53, 10);
17+
18+
return dst;
19+
}
20+
21+
Mat ImageProcessor::crop_and_transform(Mat img, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4) {
22+
float w1 = sqrt( pow(x4 - x3 , 2) + pow(x4 - x3, 2));
23+
float w2 = sqrt( pow(x2 - x1 , 2) + pow(x2-x1, 2));
24+
float h1 = sqrt( pow(y2 - y4 , 2) + pow(y2 - y4, 2));
25+
float h2 = sqrt( pow(y1 - y3 , 2) + pow(y1-y3, 2));
26+
27+
float maxWidth = (w1 < w2) ? w1 : w2;
28+
float maxHeight = (h1 < h2) ? h1 : h2;
29+
30+
Mat dst = Mat::zeros(maxHeight, maxWidth, CV_8UC1);
31+
32+
vector<Point2f> dst_pts; vector<Point2f> img_pts;
33+
dst_pts.push_back(Point(0, 0));
34+
dst_pts.push_back(Point(maxWidth - 1, 0));
35+
dst_pts.push_back(Point(0, maxHeight - 1));
36+
dst_pts.push_back(Point(maxWidth - 1, maxHeight - 1));
37+
38+
img_pts.push_back(computePoint(x1,y1));
39+
img_pts.push_back(computePoint(x2,y2));
40+
img_pts.push_back(computePoint(x3,y3));
41+
img_pts.push_back(computePoint(x4,y4));
42+
43+
Mat transformation_matrix = getPerspectiveTransform(img_pts, dst_pts);
44+
warpPerspective(img, dst, transformation_matrix, dst.size());
45+
46+
return dst;
47+
}

ios/Classes/image_processor.hpp

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#include <opencv2/opencv.hpp>
2+
3+
using namespace cv;
4+
using namespace std;
5+
6+
7+
class ImageProcessor {
8+
public:
9+
static Mat process_image(Mat img, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4);
10+
11+
private:
12+
static Mat crop_and_transform(Mat img, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4);
13+
};

ios/Classes/native_edge_detection.cpp

+30
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "native_edge_detection.hpp"
22
#include "edge_detector.hpp"
3+
#include "image_processor.hpp"
34
#include <stdlib.h>
45
#include <opencv2/opencv.hpp>
56

@@ -46,4 +47,33 @@ struct DetectionResult *detect_edges(char *str) {
4647
create_coordinate((double)points[2].x / mat.size().width, (double)points[2].y / mat.size().height),
4748
create_coordinate((double)points[3].x / mat.size().width, (double)points[3].y / mat.size().height)
4849
);
50+
}
51+
52+
extern "C" __attribute__((visibility("default"))) __attribute__((used))
53+
bool process_image(
54+
char *path,
55+
double topLeftX,
56+
double topLeftY,
57+
double topRightX,
58+
double topRightY,
59+
double bottomLeftX,
60+
double bottomLeftY,
61+
double bottomRightX,
62+
double bottomRightY
63+
) {
64+
cv::Mat mat = cv::imread(path);
65+
66+
cv::Mat resizedMat = ImageProcessor::process_image(
67+
mat,
68+
topLeftX * mat.size().width,
69+
topLeftY * mat.size().height,
70+
topRightX * mat.size().width,
71+
topRightY * mat.size().height,
72+
bottomLeftX * mat.size().width,
73+
bottomLeftY * mat.size().height,
74+
bottomRightX * mat.size().width,
75+
bottomRightY * mat.size().height
76+
);
77+
78+
return cv::imwrite(path, resizedMat);
4979
}

ios/Classes/native_edge_detection.hpp

+21-1
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,24 @@ struct DetectionResult
1313
};
1414

1515
extern "C"
16-
struct DetectionResult *detect_edges(char *str);
16+
struct ProcessingInput
17+
{
18+
char* path;
19+
DetectionResult detectionResult;
20+
};
21+
22+
extern "C"
23+
struct DetectionResult *detect_edges(char *str);
24+
25+
extern "C"
26+
bool process_image(
27+
char* path,
28+
double topLeftX,
29+
double topLeftY,
30+
double topRightX,
31+
double topRightY,
32+
double bottomLeftX,
33+
double bottomLeftY,
34+
double bottomRightX,
35+
double bottomRightY
36+
);

0 commit comments

Comments
 (0)