rn-opencv-doc-perspective-correction 1.0.4 → 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +2 -14
- package/dist/index.js +60 -35
- package/package.json +1 -1
- package/src/index.ts +67 -38
package/dist/index.d.ts
CHANGED
|
@@ -3,20 +3,8 @@ export type Point = {
|
|
|
3
3
|
y: number;
|
|
4
4
|
};
|
|
5
5
|
export declare class DocumentScanner {
|
|
6
|
-
/**
|
|
7
|
-
* Tính khoảng cách Euclidean giữa 2 điểm
|
|
8
|
-
*/
|
|
9
6
|
private static getDistance;
|
|
10
|
-
/**
|
|
11
|
-
* Sắp xếp 4 điểm thành chuỗi TL, TR, BR, BL
|
|
12
|
-
*/
|
|
13
7
|
private static sortCorners;
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
*/
|
|
17
|
-
static detectPageCorners(imageBase64: string): Point[] | undefined;
|
|
18
|
-
/**
|
|
19
|
-
* Bước 2: Perspective Correction
|
|
20
|
-
*/
|
|
21
|
-
static applyPerspectiveCorrection(imageBase64: string, corners: Point[]): string | undefined;
|
|
8
|
+
static detectPageCorners(imageBase64: string, onLog?: (msg: string) => void): Point[] | undefined;
|
|
9
|
+
static applyPerspectiveCorrection(imageBase64: string, corners: Point[], onLog?: (msg: string) => void): string | undefined;
|
|
22
10
|
}
|
package/dist/index.js
CHANGED
|
@@ -4,15 +4,9 @@ exports.DocumentScanner = void 0;
|
|
|
4
4
|
// @ts-nocheck
|
|
5
5
|
const react_native_fast_opencv_1 = require("react-native-fast-opencv");
|
|
6
6
|
class DocumentScanner {
|
|
7
|
-
/**
|
|
8
|
-
* Tính khoảng cách Euclidean giữa 2 điểm
|
|
9
|
-
*/
|
|
10
7
|
static getDistance(p1, p2) {
|
|
11
8
|
return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2));
|
|
12
9
|
}
|
|
13
|
-
/**
|
|
14
|
-
* Sắp xếp 4 điểm thành chuỗi TL, TR, BR, BL
|
|
15
|
-
*/
|
|
16
10
|
static sortCorners(corners) {
|
|
17
11
|
if (corners.length !== 4)
|
|
18
12
|
return corners;
|
|
@@ -23,10 +17,7 @@ class DocumentScanner {
|
|
|
23
17
|
return angleA - angleB;
|
|
24
18
|
});
|
|
25
19
|
}
|
|
26
|
-
|
|
27
|
-
* Bước 1: Page Corner Detection (Auto-detect góc tài liệu)
|
|
28
|
-
*/
|
|
29
|
-
static detectPageCorners(imageBase64) {
|
|
20
|
+
static detectPageCorners(imageBase64, onLog) {
|
|
30
21
|
let src = null;
|
|
31
22
|
let gray = null;
|
|
32
23
|
let blurred = null;
|
|
@@ -37,31 +28,58 @@ class DocumentScanner {
|
|
|
37
28
|
src = react_native_fast_opencv_1.OpenCV.base64ToMat(imageBase64);
|
|
38
29
|
gray = react_native_fast_opencv_1.OpenCV.createObject(react_native_fast_opencv_1.ObjectType.Mat, 0, 0, react_native_fast_opencv_1.DataTypes.CV_8U);
|
|
39
30
|
blurred = react_native_fast_opencv_1.OpenCV.createObject(react_native_fast_opencv_1.ObjectType.Mat, 0, 0, react_native_fast_opencv_1.DataTypes.CV_8U);
|
|
40
|
-
edges = react_native_fast_opencv_1.OpenCV.createObject(react_native_fast_opencv_1.ObjectType.Mat, 0, 0, react_native_fast_opencv_1.DataTypes.CV_8U);
|
|
31
|
+
edges = react_native_fast_opencv_1.OpenCV.createObject(react_native_fast_opencv_1.ObjectType.Mat, 0, 0, react_native_fast_opencv_1.DataTypes.CV_8U); // This will hold the threshold output
|
|
41
32
|
react_native_fast_opencv_1.OpenCV.invoke('cvtColor', src, gray, react_native_fast_opencv_1.ColorConversionCodes.COLOR_BGR2GRAY);
|
|
42
33
|
const ksize = react_native_fast_opencv_1.OpenCV.createObject(react_native_fast_opencv_1.ObjectType.Size, 5, 5);
|
|
43
34
|
react_native_fast_opencv_1.OpenCV.invoke('GaussianBlur', gray, blurred, ksize, 0);
|
|
44
|
-
|
|
35
|
+
// Python uses: cv2.threshold(blurred, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
|
|
36
|
+
// THRESH_BINARY = 0
|
|
37
|
+
// THRESH_OTSU = 8
|
|
38
|
+
// THRESH_BINARY + THRESH_OTSU = 8
|
|
39
|
+
react_native_fast_opencv_1.OpenCV.invoke('threshold', blurred, edges, 0, 255, 8);
|
|
45
40
|
contoursObj = react_native_fast_opencv_1.OpenCV.createObject(react_native_fast_opencv_1.ObjectType.MatVector);
|
|
46
41
|
hierarchyObj = react_native_fast_opencv_1.OpenCV.createObject(react_native_fast_opencv_1.ObjectType.Mat, 0, 0, react_native_fast_opencv_1.DataTypes.CV_8U);
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
42
|
+
// Using RETR_EXTERNAL similar to the Python script for outer contours
|
|
43
|
+
react_native_fast_opencv_1.OpenCV.invoke('findContoursWithHierarchy', edges, contoursObj, hierarchyObj, 0 /* RETR_EXTERNAL */, 2 /* CHAIN_APPROX_SIMPLE */);
|
|
44
|
+
const contoursJS = react_native_fast_opencv_1.OpenCV.toJSValue(contoursObj);
|
|
45
|
+
const contoursArray = (contoursJS === null || contoursJS === void 0 ? void 0 : contoursJS.array) || [];
|
|
46
|
+
const contoursSize = contoursArray.length;
|
|
47
|
+
if (contoursSize === 0) {
|
|
48
|
+
if (onLog)
|
|
49
|
+
onLog(`[OpenCV] Không tìm thấy contours.`);
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
// First pass: extract all areas to sort them and minimize JSI calls
|
|
53
|
+
let contourMetrics = [];
|
|
51
54
|
for (let i = 0; i < contoursSize; i++) {
|
|
52
|
-
const contour = react_native_fast_opencv_1.OpenCV.
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
react_native_fast_opencv_1.OpenCV.invoke('approxPolyDP', contour, approx, 0.02 * peri, true);
|
|
58
|
-
const approxJS = react_native_fast_opencv_1.OpenCV.toJSValue(approx);
|
|
59
|
-
if (approxJS && approxJS.array && approxJS.array.length === 4) {
|
|
60
|
-
maxArea = area;
|
|
61
|
-
largestPoly = approxJS.array;
|
|
62
|
-
}
|
|
55
|
+
const contour = react_native_fast_opencv_1.OpenCV.copyObjectFromVector(contoursObj, i);
|
|
56
|
+
const areaObj = react_native_fast_opencv_1.OpenCV.invoke('contourArea', contour);
|
|
57
|
+
const area = areaObj ? areaObj.value : 0;
|
|
58
|
+
if (area > 5000) { // filter very small artifacts
|
|
59
|
+
contourMetrics.push({ index: i, area, contour });
|
|
63
60
|
}
|
|
64
61
|
}
|
|
62
|
+
// Sort contours by area in descending order
|
|
63
|
+
contourMetrics.sort((a, b) => b.area - a.area);
|
|
64
|
+
let largestPoly = undefined;
|
|
65
|
+
// Second pass: only check approxPolyDP for the largest ones
|
|
66
|
+
for (let i = 0; i < contourMetrics.length; i++) {
|
|
67
|
+
const metric = contourMetrics[i];
|
|
68
|
+
const contour = metric.contour;
|
|
69
|
+
const periObj = react_native_fast_opencv_1.OpenCV.invoke('arcLength', contour, true);
|
|
70
|
+
const peri = periObj ? periObj.value : 0;
|
|
71
|
+
const approx = react_native_fast_opencv_1.OpenCV.createObject(react_native_fast_opencv_1.ObjectType.PointVector);
|
|
72
|
+
react_native_fast_opencv_1.OpenCV.invoke('approxPolyDP', contour, approx, 0.02 * peri, true);
|
|
73
|
+
const approxJS = react_native_fast_opencv_1.OpenCV.toJSValue(approx);
|
|
74
|
+
if (approxJS && approxJS.array && approxJS.array.length === 4) {
|
|
75
|
+
largestPoly = approxJS.array;
|
|
76
|
+
break; // Stop at the first 4-point polygon like python script
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
const logMsg = `[OpenCV] Contours: ${contoursSize}. Metrics pass: ${contourMetrics.length}. Poly detect: ${largestPoly ? 'Thành công' : 'Thất bại'}.`;
|
|
80
|
+
console.log(logMsg);
|
|
81
|
+
if (onLog)
|
|
82
|
+
onLog(logMsg);
|
|
65
83
|
if (largestPoly && largestPoly.length === 4) {
|
|
66
84
|
return this.sortCorners(largestPoly);
|
|
67
85
|
}
|
|
@@ -69,16 +87,15 @@ class DocumentScanner {
|
|
|
69
87
|
}
|
|
70
88
|
catch (e) {
|
|
71
89
|
console.error('Lỗi khi dò tìm góc tài liệu (OpenCV):', e);
|
|
72
|
-
|
|
90
|
+
if (onLog)
|
|
91
|
+
onLog(`[OpenCV Corner Detection Error]: ${e.message}`);
|
|
92
|
+
throw new Error(`[OpenCV Corner Detection Error]: ${e.message}`);
|
|
73
93
|
}
|
|
74
94
|
finally {
|
|
75
95
|
react_native_fast_opencv_1.OpenCV.clearBuffers();
|
|
76
96
|
}
|
|
77
97
|
}
|
|
78
|
-
|
|
79
|
-
* Bước 2: Perspective Correction
|
|
80
|
-
*/
|
|
81
|
-
static applyPerspectiveCorrection(imageBase64, corners) {
|
|
98
|
+
static applyPerspectiveCorrection(imageBase64, corners, onLog) {
|
|
82
99
|
let src = null;
|
|
83
100
|
let dst = null;
|
|
84
101
|
try {
|
|
@@ -108,13 +125,21 @@ class DocumentScanner {
|
|
|
108
125
|
react_native_fast_opencv_1.OpenCV.createObject(react_native_fast_opencv_1.ObjectType.Point2f, maxWidth - 1, maxHeight - 1),
|
|
109
126
|
react_native_fast_opencv_1.OpenCV.createObject(react_native_fast_opencv_1.ObjectType.Point2f, 0, maxHeight - 1)
|
|
110
127
|
]);
|
|
111
|
-
const perspectiveMatrix = react_native_fast_opencv_1.OpenCV.invoke('getPerspectiveTransform', srcPoints, dstPoints);
|
|
128
|
+
const perspectiveMatrix = react_native_fast_opencv_1.OpenCV.invoke('getPerspectiveTransform', srcPoints, dstPoints, 0);
|
|
112
129
|
const size = react_native_fast_opencv_1.OpenCV.createObject(react_native_fast_opencv_1.ObjectType.Size, maxWidth, maxHeight);
|
|
113
|
-
react_native_fast_opencv_1.OpenCV.
|
|
114
|
-
|
|
130
|
+
const borderValue = react_native_fast_opencv_1.OpenCV.createObject(react_native_fast_opencv_1.ObjectType.Scalar, 0);
|
|
131
|
+
react_native_fast_opencv_1.OpenCV.invoke('warpPerspective', src, dst, perspectiveMatrix, size, 1 /* INTER_LINEAR */, 0 /* BORDER_CONSTANT */, borderValue);
|
|
132
|
+
const dstValue = react_native_fast_opencv_1.OpenCV.toJSValue(dst);
|
|
133
|
+
// Fix "writeFile got an object" by guaranteeing string type
|
|
134
|
+
if (dstValue && dstValue.base64) {
|
|
135
|
+
return typeof dstValue.base64 === 'string' ? dstValue.base64 : String(dstValue.base64);
|
|
136
|
+
}
|
|
137
|
+
return undefined;
|
|
115
138
|
}
|
|
116
139
|
catch (e) {
|
|
117
140
|
console.error('Lỗi khi bóp phối cảnh tài liệu (OpenCV):', e);
|
|
141
|
+
if (onLog)
|
|
142
|
+
onLog(`[OpenCV Perspective Correction Error]: ${e.message || e}`);
|
|
118
143
|
return undefined;
|
|
119
144
|
}
|
|
120
145
|
finally {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rn-opencv-doc-perspective-correction",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"description": "A React Native library for document corner detection and perspective correction using react-native-fast-opencv",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
package/src/index.ts
CHANGED
|
@@ -4,16 +4,10 @@ import { OpenCV, OpenCVMat, ObjectType, DataTypes, ColorConversionCodes } from '
|
|
|
4
4
|
export type Point = { x: number; y: number };
|
|
5
5
|
|
|
6
6
|
export class DocumentScanner {
|
|
7
|
-
/**
|
|
8
|
-
* Tính khoảng cách Euclidean giữa 2 điểm
|
|
9
|
-
*/
|
|
10
7
|
private static getDistance(p1: Point, p2: Point) {
|
|
11
8
|
return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2));
|
|
12
9
|
}
|
|
13
10
|
|
|
14
|
-
/**
|
|
15
|
-
* Sắp xếp 4 điểm thành chuỗi TL, TR, BR, BL
|
|
16
|
-
*/
|
|
17
11
|
private static sortCorners(corners: Point[]): Point[] {
|
|
18
12
|
if (corners.length !== 4) return corners;
|
|
19
13
|
|
|
@@ -29,10 +23,7 @@ export class DocumentScanner {
|
|
|
29
23
|
});
|
|
30
24
|
}
|
|
31
25
|
|
|
32
|
-
|
|
33
|
-
* Bước 1: Page Corner Detection (Auto-detect góc tài liệu)
|
|
34
|
-
*/
|
|
35
|
-
public static detectPageCorners(imageBase64: string): Point[] | undefined {
|
|
26
|
+
public static detectPageCorners(imageBase64: string, onLog?: (msg: string) => void): Point[] | undefined {
|
|
36
27
|
let src: OpenCVMat | null = null;
|
|
37
28
|
let gray: OpenCVMat | null = null;
|
|
38
29
|
let blurred: OpenCVMat | null = null;
|
|
@@ -44,57 +35,85 @@ export class DocumentScanner {
|
|
|
44
35
|
src = OpenCV.base64ToMat(imageBase64);
|
|
45
36
|
gray = OpenCV.createObject(ObjectType.Mat, 0, 0, DataTypes.CV_8U);
|
|
46
37
|
blurred = OpenCV.createObject(ObjectType.Mat, 0, 0, DataTypes.CV_8U);
|
|
47
|
-
edges = OpenCV.createObject(ObjectType.Mat, 0, 0, DataTypes.CV_8U);
|
|
38
|
+
edges = OpenCV.createObject(ObjectType.Mat, 0, 0, DataTypes.CV_8U); // This will hold the threshold output
|
|
48
39
|
|
|
49
40
|
OpenCV.invoke('cvtColor', src, gray, ColorConversionCodes.COLOR_BGR2GRAY);
|
|
50
41
|
|
|
51
42
|
const ksize = OpenCV.createObject(ObjectType.Size, 5, 5);
|
|
52
43
|
OpenCV.invoke('GaussianBlur', gray, blurred, ksize, 0);
|
|
53
44
|
|
|
54
|
-
|
|
45
|
+
// Python uses: cv2.threshold(blurred, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
|
|
46
|
+
// THRESH_BINARY = 0
|
|
47
|
+
// THRESH_OTSU = 8
|
|
48
|
+
// THRESH_BINARY + THRESH_OTSU = 8
|
|
49
|
+
OpenCV.invoke('threshold', blurred, edges, 0, 255, 8);
|
|
55
50
|
|
|
56
51
|
contoursObj = OpenCV.createObject(ObjectType.MatVector);
|
|
57
52
|
hierarchyObj = OpenCV.createObject(ObjectType.Mat, 0, 0, DataTypes.CV_8U);
|
|
58
53
|
|
|
59
|
-
|
|
54
|
+
// Using RETR_EXTERNAL similar to the Python script for outer contours
|
|
55
|
+
OpenCV.invoke('findContoursWithHierarchy', edges, contoursObj, hierarchyObj, 0 /* RETR_EXTERNAL */, 2 /* CHAIN_APPROX_SIMPLE */);
|
|
60
56
|
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
57
|
+
const contoursJS = OpenCV.toJSValue(contoursObj);
|
|
58
|
+
const contoursArray = contoursJS?.array || [];
|
|
59
|
+
const contoursSize = contoursArray.length;
|
|
60
|
+
|
|
61
|
+
if (contoursSize === 0) {
|
|
62
|
+
if (onLog) onLog(`[OpenCV] Không tìm thấy contours.`);
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
64
65
|
|
|
66
|
+
// First pass: extract all areas to sort them and minimize JSI calls
|
|
67
|
+
let contourMetrics = [];
|
|
65
68
|
for (let i = 0; i < contoursSize; i++) {
|
|
66
|
-
const contour = OpenCV.
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
if (area >
|
|
70
|
-
|
|
71
|
-
const approx = OpenCV.createObject(ObjectType.PointVector);
|
|
72
|
-
OpenCV.invoke('approxPolyDP', contour, approx, 0.02 * peri, true);
|
|
73
|
-
|
|
74
|
-
const approxJS = OpenCV.toJSValue(approx);
|
|
75
|
-
if (approxJS && approxJS.array && approxJS.array.length === 4) {
|
|
76
|
-
maxArea = area;
|
|
77
|
-
largestPoly = approxJS.array as Point[];
|
|
78
|
-
}
|
|
69
|
+
const contour = OpenCV.copyObjectFromVector(contoursObj, i);
|
|
70
|
+
const areaObj = OpenCV.invoke('contourArea', contour);
|
|
71
|
+
const area = areaObj ? areaObj.value : 0;
|
|
72
|
+
if (area > 5000) { // filter very small artifacts
|
|
73
|
+
contourMetrics.push({ index: i, area, contour });
|
|
79
74
|
}
|
|
80
75
|
}
|
|
81
76
|
|
|
77
|
+
// Sort contours by area in descending order
|
|
78
|
+
contourMetrics.sort((a, b) => b.area - a.area);
|
|
79
|
+
|
|
80
|
+
let largestPoly: Point[] | undefined = undefined;
|
|
81
|
+
|
|
82
|
+
// Second pass: only check approxPolyDP for the largest ones
|
|
83
|
+
for (let i = 0; i < contourMetrics.length; i++) {
|
|
84
|
+
const metric = contourMetrics[i];
|
|
85
|
+
const contour = metric.contour;
|
|
86
|
+
|
|
87
|
+
const periObj = OpenCV.invoke('arcLength', contour, true);
|
|
88
|
+
const peri = periObj ? periObj.value : 0;
|
|
89
|
+
const approx = OpenCV.createObject(ObjectType.PointVector);
|
|
90
|
+
OpenCV.invoke('approxPolyDP', contour, approx, 0.02 * peri, true);
|
|
91
|
+
|
|
92
|
+
const approxJS = OpenCV.toJSValue(approx);
|
|
93
|
+
if (approxJS && approxJS.array && approxJS.array.length === 4) {
|
|
94
|
+
largestPoly = approxJS.array as Point[];
|
|
95
|
+
break; // Stop at the first 4-point polygon like python script
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const logMsg = `[OpenCV] Contours: ${contoursSize}. Metrics pass: ${contourMetrics.length}. Poly detect: ${largestPoly ? 'Thành công' : 'Thất bại'}.`;
|
|
100
|
+
console.log(logMsg);
|
|
101
|
+
if (onLog) onLog(logMsg);
|
|
102
|
+
|
|
82
103
|
if (largestPoly && largestPoly.length === 4) {
|
|
83
104
|
return this.sortCorners(largestPoly);
|
|
84
105
|
}
|
|
85
106
|
return undefined;
|
|
86
|
-
} catch (e) {
|
|
107
|
+
} catch (e: any) {
|
|
87
108
|
console.error('Lỗi khi dò tìm góc tài liệu (OpenCV):', e);
|
|
88
|
-
|
|
109
|
+
if (onLog) onLog(`[OpenCV Corner Detection Error]: ${e.message}`);
|
|
110
|
+
throw new Error(`[OpenCV Corner Detection Error]: ${e.message}`);
|
|
89
111
|
} finally {
|
|
90
112
|
OpenCV.clearBuffers();
|
|
91
113
|
}
|
|
92
114
|
}
|
|
93
115
|
|
|
94
|
-
|
|
95
|
-
* Bước 2: Perspective Correction
|
|
96
|
-
*/
|
|
97
|
-
public static applyPerspectiveCorrection(imageBase64: string, corners: Point[]): string | undefined {
|
|
116
|
+
public static applyPerspectiveCorrection(imageBase64: string, corners: Point[], onLog?: (msg: string) => void): string | undefined {
|
|
98
117
|
let src: OpenCVMat | null = null;
|
|
99
118
|
let dst: OpenCVMat | null = null;
|
|
100
119
|
|
|
@@ -134,17 +153,27 @@ export class DocumentScanner {
|
|
|
134
153
|
]
|
|
135
154
|
);
|
|
136
155
|
|
|
137
|
-
const perspectiveMatrix = OpenCV.invoke('getPerspectiveTransform', srcPoints, dstPoints);
|
|
156
|
+
const perspectiveMatrix = OpenCV.invoke('getPerspectiveTransform', srcPoints, dstPoints, 0);
|
|
138
157
|
|
|
139
158
|
const size = OpenCV.createObject(ObjectType.Size, maxWidth, maxHeight);
|
|
140
|
-
OpenCV.
|
|
159
|
+
const borderValue = OpenCV.createObject(ObjectType.Scalar, 0);
|
|
160
|
+
|
|
161
|
+
OpenCV.invoke('warpPerspective', src, dst, perspectiveMatrix, size, 1 /* INTER_LINEAR */, 0 /* BORDER_CONSTANT */, borderValue);
|
|
141
162
|
|
|
142
|
-
|
|
163
|
+
const dstValue = OpenCV.toJSValue(dst);
|
|
164
|
+
|
|
165
|
+
// Fix "writeFile got an object" by guaranteeing string type
|
|
166
|
+
if (dstValue && dstValue.base64) {
|
|
167
|
+
return typeof dstValue.base64 === 'string' ? dstValue.base64 : String(dstValue.base64);
|
|
168
|
+
}
|
|
169
|
+
return undefined;
|
|
143
170
|
} catch (e) {
|
|
144
171
|
console.error('Lỗi khi bóp phối cảnh tài liệu (OpenCV):', e);
|
|
172
|
+
if (onLog) onLog(`[OpenCV Perspective Correction Error]: ${(e as Error).message || e}`);
|
|
145
173
|
return undefined;
|
|
146
174
|
} finally {
|
|
147
175
|
OpenCV.clearBuffers();
|
|
148
176
|
}
|
|
149
177
|
}
|
|
150
178
|
}
|
|
179
|
+
|