@sanctum-key/react-native-sdk 1.0.58 → 1.0.60
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/build/package.json +7 -2
- package/build/src/components/EnhancedCameraView.d.ts.map +1 -1
- package/build/src/components/EnhancedCameraView.js +5 -1
- package/build/src/components/EnhancedCameraView.js.map +1 -1
- package/build/src/components/KYCElements/IDCardCapture.d.ts.map +1 -1
- package/build/src/components/KYCElements/IDCardCapture.js +34 -13
- package/build/src/components/KYCElements/IDCardCapture.js.map +1 -1
- package/build/src/components/TemplateKYCExample.d.ts +2 -1
- package/build/src/components/TemplateKYCExample.d.ts.map +1 -1
- package/build/src/components/TemplateKYCExample.js +2 -2
- package/build/src/components/TemplateKYCExample.js.map +1 -1
- package/build/src/components/TemplateKYCFlowRefactored.d.ts +1 -0
- package/build/src/components/TemplateKYCFlowRefactored.d.ts.map +1 -1
- package/build/src/components/TemplateKYCFlowRefactored.js +56 -12
- package/build/src/components/TemplateKYCFlowRefactored.js.map +1 -1
- package/build/src/modules/api/CardAuthentification.d.ts.map +1 -1
- package/build/src/modules/api/CardAuthentification.js +10 -2
- package/build/src/modules/api/CardAuthentification.js.map +1 -1
- package/build/src/utils/cropByObb.d.ts +1 -1
- package/build/src/utils/cropByObb.d.ts.map +1 -1
- package/build/src/utils/cropByObb.js +112 -5
- package/build/src/utils/cropByObb.js.map +1 -1
- package/package.json +7 -2
- package/src/components/EnhancedCameraView.tsx +4 -0
- package/src/components/KYCElements/IDCardCapture.tsx +49 -21
- package/src/components/TemplateKYCExample.tsx +4 -1
- package/src/components/TemplateKYCFlowRefactored.tsx +66 -11
- package/src/modules/api/CardAuthentification.ts +11 -3
- package/src/utils/cropByObb.ts +160 -6
package/src/utils/cropByObb.ts
CHANGED
|
@@ -4,6 +4,22 @@ import * as ImageManipulator from "expo-image-manipulator";
|
|
|
4
4
|
|
|
5
5
|
type Point = [number, number];
|
|
6
6
|
|
|
7
|
+
type PixelBbox = {
|
|
8
|
+
minX: number;
|
|
9
|
+
minY: number;
|
|
10
|
+
width: number;
|
|
11
|
+
height: number;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
type BboxOrientation = 'direct' | 'clockwise' | 'counter-clockwise' | 'upside-down';
|
|
15
|
+
|
|
16
|
+
type OrientedBboxCandidate = {
|
|
17
|
+
bbox: PixelBbox;
|
|
18
|
+
orientation: BboxOrientation;
|
|
19
|
+
inFrameRatio: number;
|
|
20
|
+
score: number;
|
|
21
|
+
};
|
|
22
|
+
|
|
7
23
|
export const OBB_CONFIDENCE_THRESHOLD = 0.85;
|
|
8
24
|
|
|
9
25
|
|
|
@@ -84,6 +100,105 @@ function computeAabb(points: Point[]) {
|
|
|
84
100
|
return { minX, minY, maxX, maxY, width: maxX - minX, height: maxY - minY };
|
|
85
101
|
}
|
|
86
102
|
|
|
103
|
+
function getInFrameRatio(bbox: PixelBbox, imageWidth: number, imageHeight: number): number {
|
|
104
|
+
const left = Math.max(0, bbox.minX);
|
|
105
|
+
const top = Math.max(0, bbox.minY);
|
|
106
|
+
const right = Math.min(imageWidth, bbox.minX + bbox.width);
|
|
107
|
+
const bottom = Math.min(imageHeight, bbox.minY + bbox.height);
|
|
108
|
+
const intersectionArea = Math.max(0, right - left) * Math.max(0, bottom - top);
|
|
109
|
+
const bboxArea = bbox.width * bbox.height;
|
|
110
|
+
|
|
111
|
+
return bboxArea > 0 ? intersectionArea / bboxArea : 0;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* The API can return OBB coordinates in the camera sensor's pixel space while
|
|
116
|
+
* ImageManipulator returns an EXIF-normalized image. On phones held sideways,
|
|
117
|
+
* those spaces are 90 degrees apart. Select the representation which best fits
|
|
118
|
+
* the normalized image instead of applying the server coordinates blindly.
|
|
119
|
+
*/
|
|
120
|
+
function resolveBboxOrientation(
|
|
121
|
+
bbox: PixelBbox,
|
|
122
|
+
imageWidth: number,
|
|
123
|
+
imageHeight: number,
|
|
124
|
+
preferredCardOrientation: 'landscape' | 'portrait' | 'either' = 'landscape',
|
|
125
|
+
): OrientedBboxCandidate {
|
|
126
|
+
const candidates: Array<{ bbox: PixelBbox; orientation: BboxOrientation; priority: number }> = [
|
|
127
|
+
{
|
|
128
|
+
bbox,
|
|
129
|
+
orientation: 'direct',
|
|
130
|
+
priority: 0.75,
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
// Source canvas is imageHeight x imageWidth and is rotated clockwise.
|
|
134
|
+
bbox: {
|
|
135
|
+
minX: imageWidth - bbox.minY - bbox.height,
|
|
136
|
+
minY: bbox.minX,
|
|
137
|
+
width: bbox.height,
|
|
138
|
+
height: bbox.width,
|
|
139
|
+
},
|
|
140
|
+
orientation: 'clockwise',
|
|
141
|
+
priority: 0,
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
// Source canvas is imageHeight x imageWidth and is rotated counter-clockwise.
|
|
145
|
+
bbox: {
|
|
146
|
+
minX: bbox.minY,
|
|
147
|
+
minY: imageHeight - bbox.minX - bbox.width,
|
|
148
|
+
width: bbox.height,
|
|
149
|
+
height: bbox.width,
|
|
150
|
+
},
|
|
151
|
+
orientation: 'counter-clockwise',
|
|
152
|
+
priority: 0,
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
bbox: {
|
|
156
|
+
minX: imageWidth - bbox.minX - bbox.width,
|
|
157
|
+
minY: imageHeight - bbox.minY - bbox.height,
|
|
158
|
+
width: bbox.width,
|
|
159
|
+
height: bbox.height,
|
|
160
|
+
},
|
|
161
|
+
orientation: 'upside-down',
|
|
162
|
+
priority: -0.25,
|
|
163
|
+
},
|
|
164
|
+
];
|
|
165
|
+
|
|
166
|
+
const scoredCandidates = candidates.map((candidate) => {
|
|
167
|
+
const candidateBbox = candidate.bbox;
|
|
168
|
+
const inFrameRatio = getInFrameRatio(candidateBbox, imageWidth, imageHeight);
|
|
169
|
+
const centerX = candidateBbox.minX + candidateBbox.width / 2;
|
|
170
|
+
const centerY = candidateBbox.minY + candidateBbox.height / 2;
|
|
171
|
+
const normalizedCenterDistance = Math.hypot(
|
|
172
|
+
(centerX - imageWidth / 2) / Math.max(1, imageWidth / 2),
|
|
173
|
+
(centerY - imageHeight / 2) / Math.max(1, imageHeight / 2),
|
|
174
|
+
);
|
|
175
|
+
const centerScore = 1 - Math.min(1, normalizedCenterDistance);
|
|
176
|
+
|
|
177
|
+
const isLandscapeCard = candidateBbox.width >= candidateBbox.height;
|
|
178
|
+
const preferredOrientationScore = preferredCardOrientation === 'either'
|
|
179
|
+
? 0
|
|
180
|
+
: ((preferredCardOrientation === 'landscape') === isLandscapeCard ? 1 : 0);
|
|
181
|
+
const areaRatio = (candidateBbox.width * candidateBbox.height) / Math.max(1, imageWidth * imageHeight);
|
|
182
|
+
const plausibleAreaScore = Math.min(1, areaRatio / 0.08);
|
|
183
|
+
|
|
184
|
+
return {
|
|
185
|
+
bbox: candidateBbox,
|
|
186
|
+
orientation: candidate.orientation,
|
|
187
|
+
inFrameRatio,
|
|
188
|
+
score:
|
|
189
|
+
inFrameRatio * 100
|
|
190
|
+
+ centerScore * 3
|
|
191
|
+
+ preferredOrientationScore * 2
|
|
192
|
+
+ plausibleAreaScore
|
|
193
|
+
+ candidate.priority,
|
|
194
|
+
};
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
return scoredCandidates.reduce((best, candidate) => (
|
|
198
|
+
candidate.score > best.score ? candidate : best
|
|
199
|
+
));
|
|
200
|
+
}
|
|
201
|
+
|
|
87
202
|
async function cropWeb(uri: string, points: Point[]): Promise<string> {
|
|
88
203
|
return new Promise((resolve, reject) => {
|
|
89
204
|
const img = new Image();
|
|
@@ -159,7 +274,12 @@ async function normalizeOrientationAndGetDimensions(
|
|
|
159
274
|
}
|
|
160
275
|
|
|
161
276
|
|
|
162
|
-
export async function cropImageWithBBoxWithTolerance(
|
|
277
|
+
export async function cropImageWithBBoxWithTolerance(
|
|
278
|
+
uri: string,
|
|
279
|
+
bbox: any,
|
|
280
|
+
tolerance: number = 0.02,
|
|
281
|
+
preferredCardOrientation: 'landscape' | 'portrait' | 'either' = 'landscape',
|
|
282
|
+
): Promise<string> {
|
|
163
283
|
return new Promise<string>((resolve) => {
|
|
164
284
|
let isResolved = false;
|
|
165
285
|
|
|
@@ -195,10 +315,44 @@ export async function cropImageWithBBoxWithTolerance(uri: string, bbox: any, tol
|
|
|
195
315
|
pixelWidth = bbox.width * actualWidth;
|
|
196
316
|
pixelHeight = bbox.height * actualHeight;
|
|
197
317
|
} else {
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
318
|
+
const resolvedBbox = resolveBboxOrientation(
|
|
319
|
+
{
|
|
320
|
+
minX: bbox.minX,
|
|
321
|
+
minY: bbox.minY,
|
|
322
|
+
width: bbox.width,
|
|
323
|
+
height: bbox.height,
|
|
324
|
+
},
|
|
325
|
+
actualWidth,
|
|
326
|
+
actualHeight,
|
|
327
|
+
preferredCardOrientation,
|
|
328
|
+
);
|
|
329
|
+
|
|
330
|
+
if (resolvedBbox.inFrameRatio < 0.6) {
|
|
331
|
+
if (!isResolved) {
|
|
332
|
+
isResolved = true;
|
|
333
|
+
clearTimeout(timeout);
|
|
334
|
+
console.warn('OBB does not match the normalized image bounds; returning the full image.', {
|
|
335
|
+
bbox,
|
|
336
|
+
imageSize: { width: actualWidth, height: actualHeight },
|
|
337
|
+
bestOrientation: resolvedBbox.orientation,
|
|
338
|
+
inFrameRatio: resolvedBbox.inFrameRatio,
|
|
339
|
+
});
|
|
340
|
+
resolve(normalizedUri);
|
|
341
|
+
}
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
if (resolvedBbox.orientation !== 'direct') {
|
|
346
|
+
console.info('Adjusted OBB coordinates for the normalized camera orientation.', {
|
|
347
|
+
orientation: resolvedBbox.orientation,
|
|
348
|
+
imageSize: { width: actualWidth, height: actualHeight },
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
pixelMinX = resolvedBbox.bbox.minX;
|
|
353
|
+
pixelMinY = resolvedBbox.bbox.minY;
|
|
354
|
+
pixelWidth = resolvedBbox.bbox.width;
|
|
355
|
+
pixelHeight = resolvedBbox.bbox.height;
|
|
202
356
|
}
|
|
203
357
|
|
|
204
358
|
// STEP 3: Add tolerance and clamp to image bounds.
|
|
@@ -309,4 +463,4 @@ export async function cropToCenterScanArea(
|
|
|
309
463
|
}
|
|
310
464
|
})();
|
|
311
465
|
});
|
|
312
|
-
}
|
|
466
|
+
}
|