capacitor-card-scanner 0.3.2 → 0.3.4
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/README.md +30 -24
- package/android/src/main/java/pt/tcgclassifier/cardscanner/CardDetector.java +523 -366
- package/android/src/main/java/pt/tcgclassifier/cardscanner/CardScannerPlugin.java +191 -167
- package/dist/docs.json +77 -10
- package/dist/esm/definitions.d.ts +39 -0
- package/dist/esm/definitions.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,167 +1,191 @@
|
|
|
1
|
-
package pt.tcgclassifier.cardscanner;
|
|
2
|
-
|
|
3
|
-
import android.Manifest;
|
|
4
|
-
import
|
|
5
|
-
import com.getcapacitor.
|
|
6
|
-
import com.getcapacitor.
|
|
7
|
-
import com.getcapacitor.
|
|
8
|
-
import com.getcapacitor.
|
|
9
|
-
import com.getcapacitor.
|
|
10
|
-
import com.getcapacitor.
|
|
11
|
-
import com.getcapacitor.annotation.
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
options.
|
|
100
|
-
options.
|
|
101
|
-
options.
|
|
102
|
-
options.
|
|
103
|
-
options.
|
|
104
|
-
options.
|
|
105
|
-
options.
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
if (!
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
1
|
+
package pt.tcgclassifier.cardscanner;
|
|
2
|
+
|
|
3
|
+
import android.Manifest;
|
|
4
|
+
import androidx.annotation.Nullable;
|
|
5
|
+
import com.getcapacitor.JSArray;
|
|
6
|
+
import com.getcapacitor.JSObject;
|
|
7
|
+
import com.getcapacitor.PermissionState;
|
|
8
|
+
import com.getcapacitor.Plugin;
|
|
9
|
+
import com.getcapacitor.PluginCall;
|
|
10
|
+
import com.getcapacitor.PluginMethod;
|
|
11
|
+
import com.getcapacitor.annotation.CapacitorPlugin;
|
|
12
|
+
import com.getcapacitor.annotation.Permission;
|
|
13
|
+
import com.getcapacitor.annotation.PermissionCallback;
|
|
14
|
+
import java.util.ArrayList;
|
|
15
|
+
import java.util.List;
|
|
16
|
+
|
|
17
|
+
@CapacitorPlugin(name = "CardScanner", permissions = { @Permission(strings = { Manifest.permission.CAMERA }, alias = CardScannerPlugin.CAMERA) })
|
|
18
|
+
public class CardScannerPlugin extends Plugin {
|
|
19
|
+
|
|
20
|
+
static final String CAMERA = "camera";
|
|
21
|
+
|
|
22
|
+
private CardScanner implementation;
|
|
23
|
+
|
|
24
|
+
@Override
|
|
25
|
+
public void load() {
|
|
26
|
+
implementation = new CardScanner(getActivity(), getBridge().getWebView());
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@Override
|
|
30
|
+
protected void handleOnDestroy() {
|
|
31
|
+
getActivity().runOnUiThread(() -> implementation.stop());
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
@PluginMethod
|
|
35
|
+
public void start(PluginCall call) {
|
|
36
|
+
if (getPermissionState(CAMERA) != PermissionState.GRANTED) {
|
|
37
|
+
requestPermissionForAlias(CAMERA, call, "cameraPermsCallback");
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
doStart(call);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
@PermissionCallback
|
|
44
|
+
private void cameraPermsCallback(PluginCall call) {
|
|
45
|
+
if (getPermissionState(CAMERA) == PermissionState.GRANTED) {
|
|
46
|
+
doStart(call);
|
|
47
|
+
} else {
|
|
48
|
+
call.reject("Camera permission denied", "PERMISSION_DENIED");
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
private void doStart(PluginCall call) {
|
|
53
|
+
boolean front = "front".equals(call.getString("position", "rear"));
|
|
54
|
+
getActivity().runOnUiThread(() -> implementation.start(front, voidCallback(call)));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
@PluginMethod
|
|
58
|
+
public void stop(PluginCall call) {
|
|
59
|
+
getActivity().runOnUiThread(() -> {
|
|
60
|
+
implementation.stop();
|
|
61
|
+
call.resolve();
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
@PluginMethod
|
|
66
|
+
public void flip(PluginCall call) {
|
|
67
|
+
if (!ensureRunning(call)) return;
|
|
68
|
+
getActivity().runOnUiThread(() -> implementation.flip(voidCallback(call)));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
@PluginMethod
|
|
72
|
+
public void capture(PluginCall call) {
|
|
73
|
+
if (!ensureRunning(call)) return;
|
|
74
|
+
int quality = Math.max(0, Math.min(100, call.getInt("quality", 85)));
|
|
75
|
+
implementation.capture(
|
|
76
|
+
quality,
|
|
77
|
+
new CardScanner.Callback<>() {
|
|
78
|
+
@Override
|
|
79
|
+
public void onSuccess(CardScanner.Photo photo) {
|
|
80
|
+
JSObject ret = new JSObject();
|
|
81
|
+
ret.put("base64", photo.base64);
|
|
82
|
+
ret.put("width", photo.width);
|
|
83
|
+
ret.put("height", photo.height);
|
|
84
|
+
call.resolve(ret);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
@Override
|
|
88
|
+
public void onError(String message, Exception e) {
|
|
89
|
+
call.reject(message, e);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
@PluginMethod
|
|
96
|
+
public void startDetection(PluginCall call) {
|
|
97
|
+
if (!ensureRunning(call)) return;
|
|
98
|
+
CardDetector.Options options = new CardDetector.Options();
|
|
99
|
+
options.intervalMs = call.getInt("intervalMs", (int) options.intervalMs);
|
|
100
|
+
options.stableFrames = call.getInt("stableFrames", options.stableFrames);
|
|
101
|
+
options.includeImage = call.getBoolean("includeImage", options.includeImage);
|
|
102
|
+
options.cardAspectRatio = call.getFloat("cardAspectRatio", options.cardAspectRatio);
|
|
103
|
+
options.aspectTolerance = call.getFloat("aspectTolerance", options.aspectTolerance);
|
|
104
|
+
options.minCardArea = call.getFloat("minCardArea", options.minCardArea);
|
|
105
|
+
options.maxCardArea = call.getFloat("maxCardArea", options.maxCardArea);
|
|
106
|
+
options.requireCard = call.getBoolean("requireCard", options.requireCard);
|
|
107
|
+
options.debug = call.getBoolean("debug", options.debug);
|
|
108
|
+
options.showRejected = call.getBoolean("showRejected", options.showRejected);
|
|
109
|
+
options.repeatAfterMs = call.getInt("repeatAfterMs", (int) options.repeatAfterMs);
|
|
110
|
+
options.nameLines = call.getInt("nameLines", options.nameLines);
|
|
111
|
+
options.cardGapMs = call.getInt("cardGapMs", (int) options.cardGapMs);
|
|
112
|
+
options.sameCardMaxDistance = call.getInt("sameCardMaxDistance", options.sameCardMaxDistance);
|
|
113
|
+
List<String> reservedWords = stringList(call, "reservedWords");
|
|
114
|
+
if (reservedWords != null) options.reservedWords = reservedWords;
|
|
115
|
+
List<String> reservedPrefixes = stringList(call, "reservedPrefixes");
|
|
116
|
+
if (reservedPrefixes != null) options.reservedPrefixes = reservedPrefixes;
|
|
117
|
+
|
|
118
|
+
CardDetector detector = new CardDetector(options, result -> notifyListeners("cardDetected", result));
|
|
119
|
+
getActivity().runOnUiThread(() -> {
|
|
120
|
+
try {
|
|
121
|
+
implementation.startDetection(detector);
|
|
122
|
+
call.resolve();
|
|
123
|
+
} catch (Exception e) {
|
|
124
|
+
detector.close();
|
|
125
|
+
call.reject("Failed to start detection: " + e.getMessage(), e);
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
@PluginMethod
|
|
131
|
+
public void stopDetection(PluginCall call) {
|
|
132
|
+
getActivity().runOnUiThread(() -> {
|
|
133
|
+
implementation.stopDetection();
|
|
134
|
+
call.resolve();
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
@PluginMethod
|
|
139
|
+
public void setTorch(PluginCall call) {
|
|
140
|
+
if (!ensureRunning(call)) return;
|
|
141
|
+
implementation.setTorch(Boolean.TRUE.equals(call.getBoolean("enabled", false)));
|
|
142
|
+
call.resolve();
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
@PluginMethod
|
|
146
|
+
public void setZoom(PluginCall call) {
|
|
147
|
+
if (!ensureRunning(call)) return;
|
|
148
|
+
Float ratio = call.getFloat("ratio");
|
|
149
|
+
if (ratio == null) {
|
|
150
|
+
call.reject("Missing 'ratio'");
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
implementation.setZoom(ratio);
|
|
154
|
+
call.resolve();
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/** A string array option, or null when it was not passed. */
|
|
158
|
+
@Nullable
|
|
159
|
+
private static List<String> stringList(PluginCall call, String key) {
|
|
160
|
+
JSArray array = call.getArray(key, null);
|
|
161
|
+
if (array == null) return null;
|
|
162
|
+
List<String> list = new ArrayList<>();
|
|
163
|
+
for (int i = 0; i < array.length(); i++) {
|
|
164
|
+
String s = array.optString(i, null);
|
|
165
|
+
if (s != null) list.add(s);
|
|
166
|
+
}
|
|
167
|
+
return list;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
private boolean ensureRunning(PluginCall call) {
|
|
171
|
+
if (!implementation.isRunning()) {
|
|
172
|
+
call.reject("Camera is not running", "NOT_RUNNING");
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
return true;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
private CardScanner.Callback<Void> voidCallback(PluginCall call) {
|
|
179
|
+
return new CardScanner.Callback<>() {
|
|
180
|
+
@Override
|
|
181
|
+
public void onSuccess(Void result) {
|
|
182
|
+
call.resolve();
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
@Override
|
|
186
|
+
public void onError(String message, Exception e) {
|
|
187
|
+
call.reject(message, e);
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
}
|
package/dist/docs.json
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
],
|
|
18
18
|
"returns": "Promise<void>",
|
|
19
19
|
"tags": [],
|
|
20
|
-
"docs": "Opens the camera **behind** the WebView. The WebView background is made\ntransparent, so the HTML page must also use transparent backgrounds\nwhere the camera should be visible.",
|
|
20
|
+
"docs": "Opens the camera **behind** the WebView. The WebView background is made\r\ntransparent, so the HTML page must also use transparent backgrounds\r\nwhere the camera should be visible.",
|
|
21
21
|
"complexTypes": [
|
|
22
22
|
"StartOptions"
|
|
23
23
|
],
|
|
@@ -110,7 +110,7 @@
|
|
|
110
110
|
],
|
|
111
111
|
"returns": "Promise<void>",
|
|
112
112
|
"tags": [],
|
|
113
|
-
"docs": "Starts looking for cards: draws a red rectangle around the card and reads\nits text, emitting `cardDetected`. Requires `start()` first. Android only.",
|
|
113
|
+
"docs": "Starts looking for cards: draws a red rectangle around the card and reads\r\nits text, emitting `cardDetected`. Requires `start()` first. Android only.",
|
|
114
114
|
"complexTypes": [
|
|
115
115
|
"DetectionOptions"
|
|
116
116
|
],
|
|
@@ -309,7 +309,7 @@
|
|
|
309
309
|
"name": "default"
|
|
310
310
|
}
|
|
311
311
|
],
|
|
312
|
-
"docs": "Minimum time between two OCR runs, in milliseconds.\nThe red rectangle is updated on every frame regardless.",
|
|
312
|
+
"docs": "Minimum time between two OCR runs, in milliseconds.\r\nThe red rectangle is updated on every frame regardless.",
|
|
313
313
|
"complexTypes": [],
|
|
314
314
|
"type": "number | undefined"
|
|
315
315
|
},
|
|
@@ -321,7 +321,7 @@
|
|
|
321
321
|
"name": "default"
|
|
322
322
|
}
|
|
323
323
|
],
|
|
324
|
-
"docs": "How many consecutive OCR runs must read the same card before\n`cardDetected` fires.",
|
|
324
|
+
"docs": "How many consecutive OCR runs must read the same card before\r\n`cardDetected` fires.",
|
|
325
325
|
"complexTypes": [],
|
|
326
326
|
"type": "number | undefined"
|
|
327
327
|
},
|
|
@@ -345,7 +345,7 @@
|
|
|
345
345
|
"name": "default"
|
|
346
346
|
}
|
|
347
347
|
],
|
|
348
|
-
"docs": "Expected width / height of an upright card. Only upright cards are\ndetected; a card lying sideways falls outside the tolerance.",
|
|
348
|
+
"docs": "Expected width / height of an upright card. Only upright cards are\r\ndetected; a card lying sideways falls outside the tolerance.",
|
|
349
349
|
"complexTypes": [],
|
|
350
350
|
"type": "number | undefined"
|
|
351
351
|
},
|
|
@@ -357,7 +357,7 @@
|
|
|
357
357
|
"name": "default"
|
|
358
358
|
}
|
|
359
359
|
],
|
|
360
|
-
"docs": "Allowed relative deviation from `cardAspectRatio` (0.15 = ±15%).\nIncrease it if tilted cards are not detected.",
|
|
360
|
+
"docs": "Allowed relative deviation from `cardAspectRatio` (0.15 = ±15%).\r\nIncrease it if tilted cards are not detected.",
|
|
361
361
|
"complexTypes": [],
|
|
362
362
|
"type": "number | undefined"
|
|
363
363
|
},
|
|
@@ -393,7 +393,7 @@
|
|
|
393
393
|
"name": "default"
|
|
394
394
|
}
|
|
395
395
|
],
|
|
396
|
-
"docs": "Only read text when a card-shaped object is visible. When false, the whole\nframe is read if no card is found.",
|
|
396
|
+
"docs": "Only read text when a card-shaped object is visible. When false, the whole\r\nframe is read if no card is found.",
|
|
397
397
|
"complexTypes": [],
|
|
398
398
|
"type": "boolean | undefined"
|
|
399
399
|
},
|
|
@@ -405,7 +405,7 @@
|
|
|
405
405
|
"name": "default"
|
|
406
406
|
}
|
|
407
407
|
],
|
|
408
|
-
"docs": "Every object found is always outlined: red when accepted as a card, grey\nwhen rejected. With `debug`, each outline is also labelled with its area\n(`a`), width/height ratio (`r`) and the filter that rejected it. Use it to\ntune the filters above.",
|
|
408
|
+
"docs": "Every object found is always outlined: red when accepted as a card, grey\r\nwhen rejected. With `debug`, each outline is also labelled with its area\r\n(`a`), width/height ratio (`r`) and the filter that rejected it. Use it to\r\ntune the filters above.",
|
|
409
409
|
"complexTypes": [],
|
|
410
410
|
"type": "boolean | undefined"
|
|
411
411
|
},
|
|
@@ -417,7 +417,7 @@
|
|
|
417
417
|
"name": "default"
|
|
418
418
|
}
|
|
419
419
|
],
|
|
420
|
-
"docs": "Outline (in grey) the objects that were not accepted as a card.\nThe accepted card is always outlined in red.",
|
|
420
|
+
"docs": "Outline (in grey) the objects that were not accepted as a card.\r\nThe accepted card is always outlined in red.",
|
|
421
421
|
"complexTypes": [],
|
|
422
422
|
"type": "boolean | undefined"
|
|
423
423
|
},
|
|
@@ -429,9 +429,69 @@
|
|
|
429
429
|
"name": "default"
|
|
430
430
|
}
|
|
431
431
|
],
|
|
432
|
-
"docs": "How long (ms) the card must be out of view before the same card can be\nreported again. While a card stays in view it is reported only once.",
|
|
432
|
+
"docs": "How long (ms) the card must be out of view before the same card can be\r\nreported again. While a card stays in view it is reported only once.",
|
|
433
433
|
"complexTypes": [],
|
|
434
434
|
"type": "number | undefined"
|
|
435
|
+
},
|
|
436
|
+
{
|
|
437
|
+
"name": "cardGapMs",
|
|
438
|
+
"tags": [
|
|
439
|
+
{
|
|
440
|
+
"text": "500",
|
|
441
|
+
"name": "default"
|
|
442
|
+
}
|
|
443
|
+
],
|
|
444
|
+
"docs": "While the card outline stays in view, at most one card is reported, even\r\nif later readings give a different name. The outline must disappear for\r\nlonger than this (ms) for the next card to be read, e.g. when swapping cards.",
|
|
445
|
+
"complexTypes": [],
|
|
446
|
+
"type": "number | undefined"
|
|
447
|
+
},
|
|
448
|
+
{
|
|
449
|
+
"name": "sameCardMaxDistance",
|
|
450
|
+
"tags": [
|
|
451
|
+
{
|
|
452
|
+
"text": "16",
|
|
453
|
+
"name": "default"
|
|
454
|
+
}
|
|
455
|
+
],
|
|
456
|
+
"docs": "Two card images whose perceptual hashes differ in at most this many bits\r\n(of 64) are treated as the same card. Lower = stricter. Lets a card slid\r\nover another one be reported without the outline disappearing.",
|
|
457
|
+
"complexTypes": [],
|
|
458
|
+
"type": "number | undefined"
|
|
459
|
+
},
|
|
460
|
+
{
|
|
461
|
+
"name": "nameLines",
|
|
462
|
+
"tags": [
|
|
463
|
+
{
|
|
464
|
+
"text": "3",
|
|
465
|
+
"name": "default"
|
|
466
|
+
}
|
|
467
|
+
],
|
|
468
|
+
"docs": "The card name is looked for only in the first N lines of text on the\r\ncard, top to bottom.",
|
|
469
|
+
"complexTypes": [],
|
|
470
|
+
"type": "number | undefined"
|
|
471
|
+
},
|
|
472
|
+
{
|
|
473
|
+
"name": "reservedWords",
|
|
474
|
+
"tags": [
|
|
475
|
+
{
|
|
476
|
+
"text": "['Basic', 'Stage', 'Stage 1', 'Stage 2', 'Trainer', 'Item', 'Supporter', 'Stadium', 'Energy', 'Special Energy', 'Basic Energy', 'Pokémon', 'Pokémon Tool', 'Tera']",
|
|
477
|
+
"name": "default"
|
|
478
|
+
}
|
|
479
|
+
],
|
|
480
|
+
"docs": "Words/phrases that are never the card name (card types, subtypes...).\r\nA line made only of these words is skipped, so \"Trainer\" or \"Stage 1\" are\r\nignored but \"Energy Retrieval\" is still a name. Replaces the defaults.\r\nAlso matched without spaces, so \"STAGE1\" counts as \"Stage 1\".",
|
|
481
|
+
"complexTypes": [],
|
|
482
|
+
"type": "string[] | undefined"
|
|
483
|
+
},
|
|
484
|
+
{
|
|
485
|
+
"name": "reservedPrefixes",
|
|
486
|
+
"tags": [
|
|
487
|
+
{
|
|
488
|
+
"text": "['Evolves from', 'HP']",
|
|
489
|
+
"name": "default"
|
|
490
|
+
}
|
|
491
|
+
],
|
|
492
|
+
"docs": "Lines starting with any of these are never the card name.\r\nReplaces the defaults.",
|
|
493
|
+
"complexTypes": [],
|
|
494
|
+
"type": "string[] | undefined"
|
|
435
495
|
}
|
|
436
496
|
]
|
|
437
497
|
},
|
|
@@ -503,6 +563,13 @@
|
|
|
503
563
|
"docs": "Base64 JPEG of the card, when `includeImage` is true.",
|
|
504
564
|
"complexTypes": [],
|
|
505
565
|
"type": "string | undefined"
|
|
566
|
+
},
|
|
567
|
+
{
|
|
568
|
+
"name": "imageHash",
|
|
569
|
+
"tags": [],
|
|
570
|
+
"docs": "64-bit perceptual hash of the card image (16 hex characters). Photos of\r\nthe same card give hashes that differ in only a few bits.",
|
|
571
|
+
"complexTypes": [],
|
|
572
|
+
"type": "string"
|
|
506
573
|
}
|
|
507
574
|
]
|
|
508
575
|
},
|
|
@@ -96,6 +96,40 @@ export interface DetectionOptions {
|
|
|
96
96
|
* @default 1500
|
|
97
97
|
*/
|
|
98
98
|
repeatAfterMs?: number;
|
|
99
|
+
/**
|
|
100
|
+
* While the card outline stays in view, at most one card is reported, even
|
|
101
|
+
* if later readings give a different name. The outline must disappear for
|
|
102
|
+
* longer than this (ms) for the next card to be read, e.g. when swapping cards.
|
|
103
|
+
* @default 500
|
|
104
|
+
*/
|
|
105
|
+
cardGapMs?: number;
|
|
106
|
+
/**
|
|
107
|
+
* Two card images whose perceptual hashes differ in at most this many bits
|
|
108
|
+
* (of 64) are treated as the same card. Lower = stricter. Lets a card slid
|
|
109
|
+
* over another one be reported without the outline disappearing.
|
|
110
|
+
* @default 16
|
|
111
|
+
*/
|
|
112
|
+
sameCardMaxDistance?: number;
|
|
113
|
+
/**
|
|
114
|
+
* The card name is looked for only in the first N lines of text on the
|
|
115
|
+
* card, top to bottom.
|
|
116
|
+
* @default 3
|
|
117
|
+
*/
|
|
118
|
+
nameLines?: number;
|
|
119
|
+
/**
|
|
120
|
+
* Words/phrases that are never the card name (card types, subtypes...).
|
|
121
|
+
* A line made only of these words is skipped, so "Trainer" or "Stage 1" are
|
|
122
|
+
* ignored but "Energy Retrieval" is still a name. Replaces the defaults.
|
|
123
|
+
* Also matched without spaces, so "STAGE1" counts as "Stage 1".
|
|
124
|
+
* @default ['Basic', 'Stage', 'Stage 1', 'Stage 2', 'Trainer', 'Item', 'Supporter', 'Stadium', 'Energy', 'Special Energy', 'Basic Energy', 'Pokémon', 'Pokémon Tool', 'Tera']
|
|
125
|
+
*/
|
|
126
|
+
reservedWords?: string[];
|
|
127
|
+
/**
|
|
128
|
+
* Lines starting with any of these are never the card name.
|
|
129
|
+
* Replaces the defaults.
|
|
130
|
+
* @default ['Evolves from', 'HP']
|
|
131
|
+
*/
|
|
132
|
+
reservedPrefixes?: string[];
|
|
99
133
|
}
|
|
100
134
|
/** Normalized rectangle (0-1). */
|
|
101
135
|
export interface Box {
|
|
@@ -121,6 +155,11 @@ export interface CardDetectedEvent {
|
|
|
121
155
|
cardBox?: Box;
|
|
122
156
|
/** Base64 JPEG of the card, when `includeImage` is true. */
|
|
123
157
|
image?: string;
|
|
158
|
+
/**
|
|
159
|
+
* 64-bit perceptual hash of the card image (16 hex characters). Photos of
|
|
160
|
+
* the same card give hashes that differ in only a few bits.
|
|
161
|
+
*/
|
|
162
|
+
imageHash: string;
|
|
124
163
|
}
|
|
125
164
|
export interface CardScannerPlugin {
|
|
126
165
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"","sourcesContent":["import type { PermissionState, PluginListenerHandle } from '@capacitor/core';\n\nexport type CameraPosition = 'rear' | 'front';\n\nexport interface StartOptions {\n /**\n * Which camera to use.\n * @default 'rear'\n */\n position?: CameraPosition;\n}\n\nexport interface CaptureOptions {\n /**\n * JPEG quality (0-100).\n * @default 85\n */\n quality?: number;\n}\n\nexport interface CaptureResult {\n /** Base64 encoded JPEG (without the `data:` prefix). */\n base64: string;\n width: number;\n height: number;\n}\n\nexport interface TorchOptions {\n enabled: boolean;\n}\n\nexport interface ZoomOptions {\n /** Zoom ratio, clamped between the camera's min and max. */\n ratio: number;\n}\n\nexport interface PermissionStatus {\n camera: PermissionState;\n}\n\nexport interface DetectionOptions {\n /**\n * Minimum time between two OCR runs, in milliseconds.\n * The red rectangle is updated on every frame regardless.\n * @default 300\n */\n intervalMs?: number;\n /**\n * How many consecutive OCR runs must read the same card before\n * `cardDetected` fires.\n * @default 2\n */\n stableFrames?: number;\n /**\n * Include a JPEG of the detected card in the event.\n * @default false\n */\n includeImage?: boolean;\n /**\n * Expected width / height of an upright card. Only upright cards are\n * detected; a card lying sideways falls outside the tolerance.\n * @default 0.716 (63 x 88 mm)\n */\n cardAspectRatio?: number;\n /**\n * Allowed relative deviation from `cardAspectRatio` (0.15 = ±15%).\n * Increase it if tilted cards are not detected.\n * @default 0.15\n */\n aspectTolerance?: number;\n /**\n * Smallest card size, as a fraction of the camera frame (0-1).\n * @default 0.08\n */\n minCardArea?: number;\n /**\n * Largest card size, as a fraction of the camera frame (0-1).\n * @default 0.9\n */\n maxCardArea?: number;\n /**\n * Only read text when a card-shaped object is visible. When false, the whole\n * frame is read if no card is found.\n * @default true\n */\n requireCard?: boolean;\n /**\n * Every object found is always outlined: red when accepted as a card, grey\n * when rejected. With `debug`, each outline is also labelled with its area\n * (`a`), width/height ratio (`r`) and the filter that rejected it. Use it to\n * tune the filters above.\n * @default false\n */\n debug?: boolean;\n /**\n * Outline (in grey) the objects that were not accepted as a card.\n * The accepted card is always outlined in red.\n * @default true\n */\n showRejected?: boolean;\n /**\n * How long (ms) the card must be out of view before the same card can be\n * reported again. While a card stays in view it is reported only once.\n * @default 1500\n */\n repeatAfterMs?: number;\n}\n\n/** Normalized rectangle (0-1). */\nexport interface Box {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\nexport interface TextLine {\n text: string;\n /** Position inside the card image. */\n box: Box;\n}\n\nexport interface CardDetectedEvent {\n /** Best guess for the card name (topmost line of text on the card). */\n name?: string;\n /** Collector number, e.g. `025/165`. */\n collectorNumber?: string;\n /** All text read on the card. */\n text: string;\n lines: TextLine[];\n /** Where the card is in the camera frame. Missing if no card outline was found. */\n cardBox?: Box;\n /** Base64 JPEG of the card, when `includeImage` is true. */\n image?: string;\n}\n\nexport interface CardScannerPlugin {\n /**\n * Opens the camera **behind** the WebView. The WebView background is made\n * transparent, so the HTML page must also use transparent backgrounds\n * where the camera should be visible.\n */\n start(options?: StartOptions): Promise<void>;\n\n /** Stops the camera and removes the preview. */\n stop(): Promise<void>;\n\n /** Takes a photo from the running camera. */\n capture(options?: CaptureOptions): Promise<CaptureResult>;\n\n /** Turns the flashlight on/off. */\n setTorch(options: TorchOptions): Promise<void>;\n\n /** Sets the zoom ratio. */\n setZoom(options: ZoomOptions): Promise<void>;\n\n /** Switches between the rear and front camera. */\n flip(): Promise<void>;\n\n /**\n * Starts looking for cards: draws a red rectangle around the card and reads\n * its text, emitting `cardDetected`. Requires `start()` first. Android only.\n */\n startDetection(options?: DetectionOptions): Promise<void>;\n\n /** Stops card detection and hides the rectangle. */\n stopDetection(): Promise<void>;\n\n /** Fired once per card, when its text has been read consistently. */\n addListener(eventName: 'cardDetected', listener: (event: CardDetectedEvent) => void): Promise<PluginListenerHandle>;\n\n removeAllListeners(): Promise<void>;\n\n checkPermissions(): Promise<PermissionStatus>;\n\n requestPermissions(): Promise<PermissionStatus>;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"","sourcesContent":["import type { PermissionState, PluginListenerHandle } from '@capacitor/core';\r\n\r\nexport type CameraPosition = 'rear' | 'front';\r\n\r\nexport interface StartOptions {\r\n /**\r\n * Which camera to use.\r\n * @default 'rear'\r\n */\r\n position?: CameraPosition;\r\n}\r\n\r\nexport interface CaptureOptions {\r\n /**\r\n * JPEG quality (0-100).\r\n * @default 85\r\n */\r\n quality?: number;\r\n}\r\n\r\nexport interface CaptureResult {\r\n /** Base64 encoded JPEG (without the `data:` prefix). */\r\n base64: string;\r\n width: number;\r\n height: number;\r\n}\r\n\r\nexport interface TorchOptions {\r\n enabled: boolean;\r\n}\r\n\r\nexport interface ZoomOptions {\r\n /** Zoom ratio, clamped between the camera's min and max. */\r\n ratio: number;\r\n}\r\n\r\nexport interface PermissionStatus {\r\n camera: PermissionState;\r\n}\r\n\r\nexport interface DetectionOptions {\r\n /**\r\n * Minimum time between two OCR runs, in milliseconds.\r\n * The red rectangle is updated on every frame regardless.\r\n * @default 300\r\n */\r\n intervalMs?: number;\r\n /**\r\n * How many consecutive OCR runs must read the same card before\r\n * `cardDetected` fires.\r\n * @default 2\r\n */\r\n stableFrames?: number;\r\n /**\r\n * Include a JPEG of the detected card in the event.\r\n * @default false\r\n */\r\n includeImage?: boolean;\r\n /**\r\n * Expected width / height of an upright card. Only upright cards are\r\n * detected; a card lying sideways falls outside the tolerance.\r\n * @default 0.716 (63 x 88 mm)\r\n */\r\n cardAspectRatio?: number;\r\n /**\r\n * Allowed relative deviation from `cardAspectRatio` (0.15 = ±15%).\r\n * Increase it if tilted cards are not detected.\r\n * @default 0.15\r\n */\r\n aspectTolerance?: number;\r\n /**\r\n * Smallest card size, as a fraction of the camera frame (0-1).\r\n * @default 0.08\r\n */\r\n minCardArea?: number;\r\n /**\r\n * Largest card size, as a fraction of the camera frame (0-1).\r\n * @default 0.9\r\n */\r\n maxCardArea?: number;\r\n /**\r\n * Only read text when a card-shaped object is visible. When false, the whole\r\n * frame is read if no card is found.\r\n * @default true\r\n */\r\n requireCard?: boolean;\r\n /**\r\n * Every object found is always outlined: red when accepted as a card, grey\r\n * when rejected. With `debug`, each outline is also labelled with its area\r\n * (`a`), width/height ratio (`r`) and the filter that rejected it. Use it to\r\n * tune the filters above.\r\n * @default false\r\n */\r\n debug?: boolean;\r\n /**\r\n * Outline (in grey) the objects that were not accepted as a card.\r\n * The accepted card is always outlined in red.\r\n * @default true\r\n */\r\n showRejected?: boolean;\r\n /**\r\n * How long (ms) the card must be out of view before the same card can be\r\n * reported again. While a card stays in view it is reported only once.\r\n * @default 1500\r\n */\r\n repeatAfterMs?: number;\r\n /**\r\n * While the card outline stays in view, at most one card is reported, even\r\n * if later readings give a different name. The outline must disappear for\r\n * longer than this (ms) for the next card to be read, e.g. when swapping cards.\r\n * @default 500\r\n */\r\n cardGapMs?: number;\r\n /**\r\n * Two card images whose perceptual hashes differ in at most this many bits\r\n * (of 64) are treated as the same card. Lower = stricter. Lets a card slid\r\n * over another one be reported without the outline disappearing.\r\n * @default 16\r\n */\r\n sameCardMaxDistance?: number;\r\n /**\r\n * The card name is looked for only in the first N lines of text on the\r\n * card, top to bottom.\r\n * @default 3\r\n */\r\n nameLines?: number;\r\n /**\r\n * Words/phrases that are never the card name (card types, subtypes...).\r\n * A line made only of these words is skipped, so \"Trainer\" or \"Stage 1\" are\r\n * ignored but \"Energy Retrieval\" is still a name. Replaces the defaults.\r\n * Also matched without spaces, so \"STAGE1\" counts as \"Stage 1\".\r\n * @default ['Basic', 'Stage', 'Stage 1', 'Stage 2', 'Trainer', 'Item', 'Supporter', 'Stadium', 'Energy', 'Special Energy', 'Basic Energy', 'Pokémon', 'Pokémon Tool', 'Tera']\r\n */\r\n reservedWords?: string[];\r\n /**\r\n * Lines starting with any of these are never the card name.\r\n * Replaces the defaults.\r\n * @default ['Evolves from', 'HP']\r\n */\r\n reservedPrefixes?: string[];\r\n}\r\n\r\n/** Normalized rectangle (0-1). */\r\nexport interface Box {\r\n x: number;\r\n y: number;\r\n width: number;\r\n height: number;\r\n}\r\n\r\nexport interface TextLine {\r\n text: string;\r\n /** Position inside the card image. */\r\n box: Box;\r\n}\r\n\r\nexport interface CardDetectedEvent {\r\n /** Best guess for the card name (topmost line of text on the card). */\r\n name?: string;\r\n /** Collector number, e.g. `025/165`. */\r\n collectorNumber?: string;\r\n /** All text read on the card. */\r\n text: string;\r\n lines: TextLine[];\r\n /** Where the card is in the camera frame. Missing if no card outline was found. */\r\n cardBox?: Box;\r\n /** Base64 JPEG of the card, when `includeImage` is true. */\r\n image?: string;\r\n /**\r\n * 64-bit perceptual hash of the card image (16 hex characters). Photos of\r\n * the same card give hashes that differ in only a few bits.\r\n */\r\n imageHash: string;\r\n}\r\n\r\nexport interface CardScannerPlugin {\r\n /**\r\n * Opens the camera **behind** the WebView. The WebView background is made\r\n * transparent, so the HTML page must also use transparent backgrounds\r\n * where the camera should be visible.\r\n */\r\n start(options?: StartOptions): Promise<void>;\r\n\r\n /** Stops the camera and removes the preview. */\r\n stop(): Promise<void>;\r\n\r\n /** Takes a photo from the running camera. */\r\n capture(options?: CaptureOptions): Promise<CaptureResult>;\r\n\r\n /** Turns the flashlight on/off. */\r\n setTorch(options: TorchOptions): Promise<void>;\r\n\r\n /** Sets the zoom ratio. */\r\n setZoom(options: ZoomOptions): Promise<void>;\r\n\r\n /** Switches between the rear and front camera. */\r\n flip(): Promise<void>;\r\n\r\n /**\r\n * Starts looking for cards: draws a red rectangle around the card and reads\r\n * its text, emitting `cardDetected`. Requires `start()` first. Android only.\r\n */\r\n startDetection(options?: DetectionOptions): Promise<void>;\r\n\r\n /** Stops card detection and hides the rectangle. */\r\n stopDetection(): Promise<void>;\r\n\r\n /** Fired once per card, when its text has been read consistently. */\r\n addListener(eventName: 'cardDetected', listener: (event: CardDetectedEvent) => void): Promise<PluginListenerHandle>;\r\n\r\n removeAllListeners(): Promise<void>;\r\n\r\n checkPermissions(): Promise<PermissionStatus>;\r\n\r\n requestPermissions(): Promise<PermissionStatus>;\r\n}\r\n"]}
|