@hexar/biometric-identity-sdk-core 1.0.0
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/BiometricIdentitySDK.d.ts +111 -0
- package/dist/BiometricIdentitySDK.js +395 -0
- package/dist/ai-models/FaceDetector.d.ts +59 -0
- package/dist/ai-models/FaceDetector.js +167 -0
- package/dist/ai-models/LivenessDetector.d.ts +61 -0
- package/dist/ai-models/LivenessDetector.js +218 -0
- package/dist/api/BackendClient.d.ts +178 -0
- package/dist/api/BackendClient.js +199 -0
- package/dist/api/index.d.ts +5 -0
- package/dist/api/index.js +8 -0
- package/dist/encryption/index.d.ts +38 -0
- package/dist/encryption/index.js +99 -0
- package/dist/i18n/index.d.ts +6 -0
- package/dist/i18n/index.js +47 -0
- package/dist/i18n/languages/en.d.ts +2 -0
- package/dist/i18n/languages/en.js +112 -0
- package/dist/i18n/languages/es-AR.d.ts +2 -0
- package/dist/i18n/languages/es-AR.js +112 -0
- package/dist/i18n/languages/es.d.ts +2 -0
- package/dist/i18n/languages/es.js +112 -0
- package/dist/i18n/languages/pt-BR.d.ts +2 -0
- package/dist/i18n/languages/pt-BR.js +112 -0
- package/dist/i18n/types.d.ts +110 -0
- package/dist/i18n/types.js +2 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +64 -0
- package/dist/services/BackendValidationService.d.ts +84 -0
- package/dist/services/BackendValidationService.js +174 -0
- package/dist/services/IValidationService.d.ts +132 -0
- package/dist/services/IValidationService.js +8 -0
- package/dist/services/index.d.ts +8 -0
- package/dist/services/index.js +10 -0
- package/dist/types/index.d.ts +288 -0
- package/dist/types/index.js +34 -0
- package/dist/validation/DocumentValidator.d.ts +84 -0
- package/dist/validation/DocumentValidator.js +295 -0
- package/dist/validation/OCREngine.d.ts +75 -0
- package/dist/validation/OCREngine.js +225 -0
- package/package.json +24 -0
- package/src/BiometricIdentitySDK.ts +493 -0
- package/src/ai-models/FaceDetector.ts +200 -0
- package/src/ai-models/LivenessDetector.ts +274 -0
- package/src/api/BackendClient.ts +395 -0
- package/src/api/index.ts +15 -0
- package/src/encryption/index.ts +108 -0
- package/src/i18n/index.ts +35 -0
- package/src/i18n/languages/en.ts +121 -0
- package/src/i18n/languages/es-AR.ts +121 -0
- package/src/i18n/languages/es.ts +121 -0
- package/src/i18n/languages/pt-BR.ts +121 -0
- package/src/i18n/types.ts +121 -0
- package/src/index.ts +54 -0
- package/src/services/BackendValidationService.ts +228 -0
- package/src/services/IValidationService.ts +158 -0
- package/src/services/index.ts +17 -0
- package/src/types/index.ts +380 -0
- package/src/validation/DocumentValidator.ts +353 -0
- package/src/validation/OCREngine.ts +265 -0
- package/tsconfig.json +20 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Document Validation - Detect tampering, quality issues, and authenticity
|
|
3
|
+
*/
|
|
4
|
+
import { DocumentValidationResult } from '../types';
|
|
5
|
+
export declare class DocumentValidator {
|
|
6
|
+
private modelPath?;
|
|
7
|
+
private model;
|
|
8
|
+
private isModelLoaded;
|
|
9
|
+
constructor(modelPath?: string | undefined);
|
|
10
|
+
/**
|
|
11
|
+
* Load document validation models
|
|
12
|
+
*/
|
|
13
|
+
loadModel(): Promise<void>;
|
|
14
|
+
/**
|
|
15
|
+
* Validate document authenticity and quality
|
|
16
|
+
*/
|
|
17
|
+
validateDocument(imageData: string | Buffer): Promise<DocumentValidationResult>;
|
|
18
|
+
/**
|
|
19
|
+
* Check document image quality
|
|
20
|
+
*/
|
|
21
|
+
private checkQuality;
|
|
22
|
+
/**
|
|
23
|
+
* Detect glare in document image
|
|
24
|
+
*/
|
|
25
|
+
private detectGlare;
|
|
26
|
+
/**
|
|
27
|
+
* Detect blur in document image
|
|
28
|
+
*/
|
|
29
|
+
private detectBlur;
|
|
30
|
+
/**
|
|
31
|
+
* Detect if document is cropped
|
|
32
|
+
*/
|
|
33
|
+
private detectCropping;
|
|
34
|
+
/**
|
|
35
|
+
* Detect reflections on document
|
|
36
|
+
*/
|
|
37
|
+
private detectReflections;
|
|
38
|
+
/**
|
|
39
|
+
* Calculate edge detection score
|
|
40
|
+
*/
|
|
41
|
+
private calculateEdgeScore;
|
|
42
|
+
/**
|
|
43
|
+
* Detect document tampering
|
|
44
|
+
*/
|
|
45
|
+
private detectTampering;
|
|
46
|
+
/**
|
|
47
|
+
* Calculate tampering score
|
|
48
|
+
*/
|
|
49
|
+
private calculateTamperScore;
|
|
50
|
+
/**
|
|
51
|
+
* Find suspicious regions in document
|
|
52
|
+
*/
|
|
53
|
+
private findSuspiciousRegions;
|
|
54
|
+
/**
|
|
55
|
+
* Detect hologram on document
|
|
56
|
+
*/
|
|
57
|
+
private detectHologram;
|
|
58
|
+
/**
|
|
59
|
+
* Calculate overall authenticity score
|
|
60
|
+
*/
|
|
61
|
+
private calculateAuthenticityScore;
|
|
62
|
+
/**
|
|
63
|
+
* Detect document type (passport, ID card, driver's license, etc.)
|
|
64
|
+
*/
|
|
65
|
+
detectDocumentType(imageData: string | Buffer): Promise<string>;
|
|
66
|
+
/**
|
|
67
|
+
* Validate document expiration
|
|
68
|
+
*/
|
|
69
|
+
validateExpiration(expirationDate: string): boolean;
|
|
70
|
+
/**
|
|
71
|
+
* Clean up resources
|
|
72
|
+
*/
|
|
73
|
+
dispose(): void;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Validate document meets minimum quality standards
|
|
77
|
+
*/
|
|
78
|
+
export declare function meetsQualityStandards(quality: {
|
|
79
|
+
hasGlare: boolean;
|
|
80
|
+
hasBlur: boolean;
|
|
81
|
+
hasCropping: boolean;
|
|
82
|
+
hasReflections: boolean;
|
|
83
|
+
edgeScore: number;
|
|
84
|
+
}): boolean;
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Document Validation - Detect tampering, quality issues, and authenticity
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.DocumentValidator = void 0;
|
|
7
|
+
exports.meetsQualityStandards = meetsQualityStandards;
|
|
8
|
+
class DocumentValidator {
|
|
9
|
+
constructor(modelPath) {
|
|
10
|
+
this.modelPath = modelPath;
|
|
11
|
+
this.isModelLoaded = false;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Load document validation models
|
|
15
|
+
*/
|
|
16
|
+
async loadModel() {
|
|
17
|
+
if (this.isModelLoaded)
|
|
18
|
+
return;
|
|
19
|
+
try {
|
|
20
|
+
console.log('Loading document validation model...');
|
|
21
|
+
// Simulate model loading
|
|
22
|
+
await new Promise(resolve => setTimeout(resolve, 300));
|
|
23
|
+
this.isModelLoaded = true;
|
|
24
|
+
console.log('Document validation model loaded successfully');
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
throw new Error(`Failed to load document validation model: ${error}`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Validate document authenticity and quality
|
|
32
|
+
*/
|
|
33
|
+
async validateDocument(imageData) {
|
|
34
|
+
if (!this.isModelLoaded) {
|
|
35
|
+
await this.loadModel();
|
|
36
|
+
}
|
|
37
|
+
try {
|
|
38
|
+
// Perform parallel quality checks
|
|
39
|
+
const [qualityResult, tamperResult, hologramResult] = await Promise.all([
|
|
40
|
+
this.checkQuality(imageData),
|
|
41
|
+
this.detectTampering(imageData),
|
|
42
|
+
this.detectHologram(imageData),
|
|
43
|
+
]);
|
|
44
|
+
// Calculate overall authenticity score
|
|
45
|
+
const authenticityScore = this.calculateAuthenticityScore(qualityResult, tamperResult, hologramResult);
|
|
46
|
+
const isAuthentic = authenticityScore >= 70 && !tamperResult.detected;
|
|
47
|
+
return {
|
|
48
|
+
isAuthentic,
|
|
49
|
+
authenticityScore,
|
|
50
|
+
quality: qualityResult,
|
|
51
|
+
tamper: tamperResult,
|
|
52
|
+
hologram: hologramResult,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
throw new Error(`Document validation failed: ${error}`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Check document image quality
|
|
61
|
+
*/
|
|
62
|
+
async checkQuality(imageData) {
|
|
63
|
+
// In real implementation:
|
|
64
|
+
// 1. Detect bright spots (glare)
|
|
65
|
+
// 2. Calculate blur metric (Laplacian variance)
|
|
66
|
+
// 3. Detect document edges and check if complete
|
|
67
|
+
// 4. Detect reflective areas
|
|
68
|
+
const hasGlare = await this.detectGlare(imageData);
|
|
69
|
+
const hasBlur = await this.detectBlur(imageData);
|
|
70
|
+
const hasCropping = await this.detectCropping(imageData);
|
|
71
|
+
const hasReflections = await this.detectReflections(imageData);
|
|
72
|
+
const edgeScore = await this.calculateEdgeScore(imageData);
|
|
73
|
+
return {
|
|
74
|
+
hasGlare,
|
|
75
|
+
hasBlur,
|
|
76
|
+
hasCropping,
|
|
77
|
+
hasReflections,
|
|
78
|
+
edgeScore,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Detect glare in document image
|
|
83
|
+
*/
|
|
84
|
+
async detectGlare(imageData) {
|
|
85
|
+
// In real implementation:
|
|
86
|
+
// 1. Convert to grayscale
|
|
87
|
+
// 2. Find pixels above high threshold (> 240)
|
|
88
|
+
// 3. Calculate percentage of bright pixels
|
|
89
|
+
// 4. Check if bright regions form patches (glare patterns)
|
|
90
|
+
// Mock implementation
|
|
91
|
+
const randomScore = Math.random();
|
|
92
|
+
return randomScore < 0.15; // 15% chance of glare
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Detect blur in document image
|
|
96
|
+
*/
|
|
97
|
+
async detectBlur(imageData) {
|
|
98
|
+
// In real implementation:
|
|
99
|
+
// 1. Apply Laplacian operator
|
|
100
|
+
// 2. Calculate variance of Laplacian
|
|
101
|
+
// 3. Low variance indicates blur
|
|
102
|
+
// Mock implementation
|
|
103
|
+
const randomScore = Math.random();
|
|
104
|
+
return randomScore < 0.10; // 10% chance of blur
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Detect if document is cropped
|
|
108
|
+
*/
|
|
109
|
+
async detectCropping(imageData) {
|
|
110
|
+
// In real implementation:
|
|
111
|
+
// 1. Detect document edges using Canny edge detection
|
|
112
|
+
// 2. Find document contours
|
|
113
|
+
// 3. Check if all four corners are visible
|
|
114
|
+
// Mock implementation
|
|
115
|
+
const randomScore = Math.random();
|
|
116
|
+
return randomScore < 0.08; // 8% chance of cropping
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Detect reflections on document
|
|
120
|
+
*/
|
|
121
|
+
async detectReflections(imageData) {
|
|
122
|
+
// In real implementation:
|
|
123
|
+
// 1. Analyze specular highlights
|
|
124
|
+
// 2. Detect glossy surface patterns
|
|
125
|
+
// 3. Check for uneven lighting
|
|
126
|
+
// Mock implementation
|
|
127
|
+
const randomScore = Math.random();
|
|
128
|
+
return randomScore < 0.12; // 12% chance of reflections
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Calculate edge detection score
|
|
132
|
+
*/
|
|
133
|
+
async calculateEdgeScore(imageData) {
|
|
134
|
+
// In real implementation:
|
|
135
|
+
// 1. Apply Canny edge detection
|
|
136
|
+
// 2. Calculate edge pixel density
|
|
137
|
+
// 3. Normalize to 0-100 scale
|
|
138
|
+
// Mock implementation - random score between 70-98
|
|
139
|
+
return Math.round(70 + Math.random() * 28);
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Detect document tampering
|
|
143
|
+
*/
|
|
144
|
+
async detectTampering(imageData) {
|
|
145
|
+
// In real implementation:
|
|
146
|
+
// 1. Error Level Analysis (ELA)
|
|
147
|
+
// 2. Clone detection
|
|
148
|
+
// 3. JPEG artifact analysis
|
|
149
|
+
// 4. Metadata analysis
|
|
150
|
+
// 5. Font consistency check
|
|
151
|
+
// 6. Color histogram analysis
|
|
152
|
+
const tamperScore = await this.calculateTamperScore(imageData);
|
|
153
|
+
const suspiciousRegions = await this.findSuspiciousRegions(imageData);
|
|
154
|
+
return {
|
|
155
|
+
detected: tamperScore > 60,
|
|
156
|
+
score: tamperScore,
|
|
157
|
+
regions: suspiciousRegions,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Calculate tampering score
|
|
162
|
+
*/
|
|
163
|
+
async calculateTamperScore(imageData) {
|
|
164
|
+
// In real implementation:
|
|
165
|
+
// 1. Analyze compression artifacts
|
|
166
|
+
// 2. Check for inconsistent noise patterns
|
|
167
|
+
// 3. Detect copy-paste regions
|
|
168
|
+
// 4. Validate text rendering consistency
|
|
169
|
+
// Mock implementation - most documents are authentic
|
|
170
|
+
const randomScore = Math.random();
|
|
171
|
+
return randomScore < 0.95 ? Math.round(Math.random() * 30) : Math.round(60 + Math.random() * 40);
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Find suspicious regions in document
|
|
175
|
+
*/
|
|
176
|
+
async findSuspiciousRegions(imageData) {
|
|
177
|
+
// In real implementation:
|
|
178
|
+
// 1. Apply ELA and find high-error regions
|
|
179
|
+
// 2. Detect cloned regions
|
|
180
|
+
// 3. Find inconsistent JPEG blocks
|
|
181
|
+
// Mock implementation - usually no suspicious regions
|
|
182
|
+
const randomScore = Math.random();
|
|
183
|
+
if (randomScore < 0.95) {
|
|
184
|
+
return [];
|
|
185
|
+
}
|
|
186
|
+
// Return 1-2 suspicious regions
|
|
187
|
+
return [
|
|
188
|
+
{
|
|
189
|
+
x: Math.round(Math.random() * 200),
|
|
190
|
+
y: Math.round(Math.random() * 100),
|
|
191
|
+
width: 50 + Math.round(Math.random() * 100),
|
|
192
|
+
height: 30 + Math.round(Math.random() * 50),
|
|
193
|
+
},
|
|
194
|
+
];
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Detect hologram on document
|
|
198
|
+
*/
|
|
199
|
+
async detectHologram(imageData) {
|
|
200
|
+
// In real implementation:
|
|
201
|
+
// 1. Detect rainbow color patterns
|
|
202
|
+
// 2. Analyze light reflection patterns
|
|
203
|
+
// 3. Check for 3D effects
|
|
204
|
+
// 4. Validate hologram location on document
|
|
205
|
+
// Mock implementation
|
|
206
|
+
const randomScore = Math.random();
|
|
207
|
+
if (randomScore > 0.3) {
|
|
208
|
+
return {
|
|
209
|
+
detected: true,
|
|
210
|
+
confidence: 75 + Math.round(Math.random() * 20),
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
return {
|
|
214
|
+
detected: false,
|
|
215
|
+
confidence: Math.round(Math.random() * 50),
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Calculate overall authenticity score
|
|
220
|
+
*/
|
|
221
|
+
calculateAuthenticityScore(quality, tamper, hologram) {
|
|
222
|
+
let score = 100;
|
|
223
|
+
// Deduct points for quality issues
|
|
224
|
+
if (quality.hasGlare)
|
|
225
|
+
score -= 15;
|
|
226
|
+
if (quality.hasBlur)
|
|
227
|
+
score -= 20;
|
|
228
|
+
if (quality.hasCropping)
|
|
229
|
+
score -= 25;
|
|
230
|
+
if (quality.hasReflections)
|
|
231
|
+
score -= 10;
|
|
232
|
+
// Edge score impact
|
|
233
|
+
if (quality.edgeScore < 70)
|
|
234
|
+
score -= 15;
|
|
235
|
+
else if (quality.edgeScore < 80)
|
|
236
|
+
score -= 10;
|
|
237
|
+
// Tampering severely impacts score
|
|
238
|
+
if (tamper.detected) {
|
|
239
|
+
score -= 40;
|
|
240
|
+
}
|
|
241
|
+
else {
|
|
242
|
+
score -= Math.round(tamper.score * 0.3);
|
|
243
|
+
}
|
|
244
|
+
// Hologram bonus (if detected and confident)
|
|
245
|
+
if (hologram?.detected && hologram.confidence > 70) {
|
|
246
|
+
score = Math.min(100, score + 5);
|
|
247
|
+
}
|
|
248
|
+
return Math.max(0, Math.round(score));
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Detect document type (passport, ID card, driver's license, etc.)
|
|
252
|
+
*/
|
|
253
|
+
async detectDocumentType(imageData) {
|
|
254
|
+
// In real implementation:
|
|
255
|
+
// 1. Use template matching
|
|
256
|
+
// 2. Detect document layout patterns
|
|
257
|
+
// 3. OCR specific regions to identify type
|
|
258
|
+
// Mock implementation
|
|
259
|
+
const types = ['passport', 'national_id', 'drivers_license'];
|
|
260
|
+
return types[Math.floor(Math.random() * types.length)];
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Validate document expiration
|
|
264
|
+
*/
|
|
265
|
+
validateExpiration(expirationDate) {
|
|
266
|
+
try {
|
|
267
|
+
const expiry = new Date(expirationDate);
|
|
268
|
+
const now = new Date();
|
|
269
|
+
return expiry > now;
|
|
270
|
+
}
|
|
271
|
+
catch {
|
|
272
|
+
return false;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Clean up resources
|
|
277
|
+
*/
|
|
278
|
+
dispose() {
|
|
279
|
+
this.model = null;
|
|
280
|
+
this.isModelLoaded = false;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
exports.DocumentValidator = DocumentValidator;
|
|
284
|
+
/**
|
|
285
|
+
* Validate document meets minimum quality standards
|
|
286
|
+
*/
|
|
287
|
+
function meetsQualityStandards(quality) {
|
|
288
|
+
if (quality.hasBlur || quality.hasCropping)
|
|
289
|
+
return false;
|
|
290
|
+
if (quality.hasGlare && quality.hasReflections)
|
|
291
|
+
return false;
|
|
292
|
+
if (quality.edgeScore < 60)
|
|
293
|
+
return false;
|
|
294
|
+
return true;
|
|
295
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OCR Engine for extracting data from ID documents
|
|
3
|
+
*/
|
|
4
|
+
import { DocumentData } from '../types';
|
|
5
|
+
export interface OCRResult {
|
|
6
|
+
text: string;
|
|
7
|
+
confidence: number;
|
|
8
|
+
boundingBox?: {
|
|
9
|
+
x: number;
|
|
10
|
+
y: number;
|
|
11
|
+
width: number;
|
|
12
|
+
height: number;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export declare class OCREngine {
|
|
16
|
+
private tesseract;
|
|
17
|
+
private isInitialized;
|
|
18
|
+
constructor();
|
|
19
|
+
/**
|
|
20
|
+
* Initialize OCR engine
|
|
21
|
+
*/
|
|
22
|
+
initialize(): Promise<void>;
|
|
23
|
+
/**
|
|
24
|
+
* Extract text from document image
|
|
25
|
+
*/
|
|
26
|
+
extractText(imageData: string | Buffer): Promise<OCRResult[]>;
|
|
27
|
+
/**
|
|
28
|
+
* Extract structured data from ID document
|
|
29
|
+
*/
|
|
30
|
+
extractDocumentData(frontImage: string | Buffer, backImage?: string | Buffer): Promise<DocumentData>;
|
|
31
|
+
/**
|
|
32
|
+
* Parse raw OCR text into structured document data
|
|
33
|
+
*/
|
|
34
|
+
private parseDocumentData;
|
|
35
|
+
/**
|
|
36
|
+
* Extract field from text using regex
|
|
37
|
+
*/
|
|
38
|
+
private extractField;
|
|
39
|
+
/**
|
|
40
|
+
* Extract and format date from text
|
|
41
|
+
*/
|
|
42
|
+
private extractDate;
|
|
43
|
+
/**
|
|
44
|
+
* Extract gender from text
|
|
45
|
+
*/
|
|
46
|
+
private extractGender;
|
|
47
|
+
/**
|
|
48
|
+
* Generate mock document number
|
|
49
|
+
*/
|
|
50
|
+
private generateMockDocNumber;
|
|
51
|
+
/**
|
|
52
|
+
* Generate mock expiration date (2-5 years from now)
|
|
53
|
+
*/
|
|
54
|
+
private generateExpirationDate;
|
|
55
|
+
/**
|
|
56
|
+
* Mock OCR results
|
|
57
|
+
*/
|
|
58
|
+
private mockOCRResults;
|
|
59
|
+
/**
|
|
60
|
+
* Validate extracted document number format
|
|
61
|
+
*/
|
|
62
|
+
validateDocumentNumber(documentNumber: string, documentType: string): boolean;
|
|
63
|
+
/**
|
|
64
|
+
* Clean up resources
|
|
65
|
+
*/
|
|
66
|
+
dispose(): Promise<void>;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Clean and normalize extracted text
|
|
70
|
+
*/
|
|
71
|
+
export declare function cleanText(text: string): string;
|
|
72
|
+
/**
|
|
73
|
+
* Validate date format
|
|
74
|
+
*/
|
|
75
|
+
export declare function isValidDate(dateString: string): boolean;
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* OCR Engine for extracting data from ID documents
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.OCREngine = void 0;
|
|
7
|
+
exports.cleanText = cleanText;
|
|
8
|
+
exports.isValidDate = isValidDate;
|
|
9
|
+
class OCREngine {
|
|
10
|
+
constructor() {
|
|
11
|
+
this.isInitialized = false;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Initialize OCR engine
|
|
15
|
+
*/
|
|
16
|
+
async initialize() {
|
|
17
|
+
if (this.isInitialized)
|
|
18
|
+
return;
|
|
19
|
+
try {
|
|
20
|
+
console.log('Initializing OCR engine...');
|
|
21
|
+
// In real implementation, would initialize Tesseract.js
|
|
22
|
+
// await createWorker();
|
|
23
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
24
|
+
this.isInitialized = true;
|
|
25
|
+
console.log('OCR engine initialized successfully');
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
throw new Error(`Failed to initialize OCR engine: ${error}`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Extract text from document image
|
|
33
|
+
*/
|
|
34
|
+
async extractText(imageData) {
|
|
35
|
+
if (!this.isInitialized) {
|
|
36
|
+
await this.initialize();
|
|
37
|
+
}
|
|
38
|
+
try {
|
|
39
|
+
// In real implementation:
|
|
40
|
+
// const result = await this.tesseract.recognize(imageData);
|
|
41
|
+
// return result.data.words;
|
|
42
|
+
// Mock implementation
|
|
43
|
+
return this.mockOCRResults();
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
throw new Error(`OCR extraction failed: ${error}`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Extract structured data from ID document
|
|
51
|
+
*/
|
|
52
|
+
async extractDocumentData(frontImage, backImage) {
|
|
53
|
+
if (!this.isInitialized) {
|
|
54
|
+
await this.initialize();
|
|
55
|
+
}
|
|
56
|
+
try {
|
|
57
|
+
// Extract text from front
|
|
58
|
+
const frontText = await this.extractText(frontImage);
|
|
59
|
+
const frontRawText = frontText.map(r => r.text).join(' ');
|
|
60
|
+
// Extract text from back (if provided)
|
|
61
|
+
let backRawText = '';
|
|
62
|
+
if (backImage) {
|
|
63
|
+
const backText = await this.extractText(backImage);
|
|
64
|
+
backRawText = backText.map(r => r.text).join(' ');
|
|
65
|
+
}
|
|
66
|
+
// Parse structured data
|
|
67
|
+
const documentData = this.parseDocumentData(frontRawText, backRawText);
|
|
68
|
+
return documentData;
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
throw new Error(`Document data extraction failed: ${error}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Parse raw OCR text into structured document data
|
|
76
|
+
*/
|
|
77
|
+
parseDocumentData(frontText, backText) {
|
|
78
|
+
// In real implementation:
|
|
79
|
+
// 1. Use regex patterns for different document types
|
|
80
|
+
// 2. Apply NLP for field extraction
|
|
81
|
+
// 3. Validate extracted data format
|
|
82
|
+
// 4. Handle multiple languages
|
|
83
|
+
// Mock implementation with realistic patterns
|
|
84
|
+
const firstName = this.extractField(frontText, /(?:Given\s+Name|First\s+Name|Nombre)[:\s]+([A-Z][a-z]+)/i) || 'John';
|
|
85
|
+
const lastName = this.extractField(frontText, /(?:Surname|Last\s+Name|Apellido)[:\s]+([A-Z][a-z]+)/i) || 'Doe';
|
|
86
|
+
const documentNumber = this.extractField(frontText, /(?:ID|Document)\s*(?:No|Number|#)[:\s]*([A-Z0-9]{6,12})/i) || this.generateMockDocNumber();
|
|
87
|
+
const dateOfBirth = this.extractDate(frontText, /(?:Date\s+of\s+Birth|DOB|Fecha\s+de\s+Nacimiento)[:\s]*([\d\/\-\.]+)/i) || '1990-01-15';
|
|
88
|
+
const expirationDate = this.extractDate(frontText, /(?:Expir(?:y|ation)|Valid\s+Until)[:\s]*([\d\/\-\.]+)/i) || this.generateExpirationDate();
|
|
89
|
+
const nationality = this.extractField(frontText, /(?:Nationality|Nacionalidad)[:\s]+([A-Z]{2,20})/i) || 'USA';
|
|
90
|
+
return {
|
|
91
|
+
firstName,
|
|
92
|
+
lastName,
|
|
93
|
+
documentNumber,
|
|
94
|
+
dateOfBirth,
|
|
95
|
+
expirationDate,
|
|
96
|
+
nationality,
|
|
97
|
+
documentType: 'national_id',
|
|
98
|
+
gender: this.extractGender(frontText),
|
|
99
|
+
rawText: frontText + ' ' + backText,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Extract field from text using regex
|
|
104
|
+
*/
|
|
105
|
+
extractField(text, pattern) {
|
|
106
|
+
const match = text.match(pattern);
|
|
107
|
+
return match ? match[1].trim() : null;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Extract and format date from text
|
|
111
|
+
*/
|
|
112
|
+
extractDate(text, pattern) {
|
|
113
|
+
const match = text.match(pattern);
|
|
114
|
+
if (!match)
|
|
115
|
+
return null;
|
|
116
|
+
const dateStr = match[1].trim();
|
|
117
|
+
// Try to parse date
|
|
118
|
+
try {
|
|
119
|
+
// Handle various date formats: DD/MM/YYYY, MM-DD-YYYY, DD.MM.YYYY, etc.
|
|
120
|
+
const parts = dateStr.split(/[\-\/\.]/);
|
|
121
|
+
if (parts.length === 3) {
|
|
122
|
+
// Assume DD/MM/YYYY or MM/DD/YYYY
|
|
123
|
+
let day, month, year;
|
|
124
|
+
if (parseInt(parts[0]) > 12) {
|
|
125
|
+
// DD/MM/YYYY
|
|
126
|
+
day = parts[0].padStart(2, '0');
|
|
127
|
+
month = parts[1].padStart(2, '0');
|
|
128
|
+
year = parts[2];
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
// MM/DD/YYYY
|
|
132
|
+
month = parts[0].padStart(2, '0');
|
|
133
|
+
day = parts[1].padStart(2, '0');
|
|
134
|
+
year = parts[2];
|
|
135
|
+
}
|
|
136
|
+
return `${year}-${month}-${day}`;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
return dateStr;
|
|
141
|
+
}
|
|
142
|
+
return dateStr;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Extract gender from text
|
|
146
|
+
*/
|
|
147
|
+
extractGender(text) {
|
|
148
|
+
const match = text.match(/(?:Sex|Gender|Sexo)[:\s]*([MF])/i);
|
|
149
|
+
if (match) {
|
|
150
|
+
return match[1].toUpperCase() === 'M' ? 'Male' : 'Female';
|
|
151
|
+
}
|
|
152
|
+
return undefined;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Generate mock document number
|
|
156
|
+
*/
|
|
157
|
+
generateMockDocNumber() {
|
|
158
|
+
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
|
159
|
+
let result = '';
|
|
160
|
+
for (let i = 0; i < 9; i++) {
|
|
161
|
+
result += chars.charAt(Math.floor(Math.random() * chars.length));
|
|
162
|
+
}
|
|
163
|
+
return result;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Generate mock expiration date (2-5 years from now)
|
|
167
|
+
*/
|
|
168
|
+
generateExpirationDate() {
|
|
169
|
+
const date = new Date();
|
|
170
|
+
date.setFullYear(date.getFullYear() + 2 + Math.floor(Math.random() * 3));
|
|
171
|
+
return date.toISOString().split('T')[0];
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Mock OCR results
|
|
175
|
+
*/
|
|
176
|
+
mockOCRResults() {
|
|
177
|
+
return [
|
|
178
|
+
{ text: 'UNITED STATES OF AMERICA', confidence: 0.95 },
|
|
179
|
+
{ text: 'Driver License', confidence: 0.92 },
|
|
180
|
+
{ text: 'First Name: JOHN', confidence: 0.88 },
|
|
181
|
+
{ text: 'Last Name: DOE', confidence: 0.90 },
|
|
182
|
+
{ text: 'DOB: 01/15/1990', confidence: 0.87 },
|
|
183
|
+
{ text: 'Document No: DL123456789', confidence: 0.85 },
|
|
184
|
+
{ text: 'Expiration: 12/31/2026', confidence: 0.89 },
|
|
185
|
+
{ text: 'Nationality: USA', confidence: 0.93 },
|
|
186
|
+
];
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Validate extracted document number format
|
|
190
|
+
*/
|
|
191
|
+
validateDocumentNumber(documentNumber, documentType) {
|
|
192
|
+
// In real implementation, validate format based on document type and country
|
|
193
|
+
if (!documentNumber || documentNumber.length < 6)
|
|
194
|
+
return false;
|
|
195
|
+
// Basic alphanumeric check
|
|
196
|
+
return /^[A-Z0-9]{6,15}$/i.test(documentNumber);
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Clean up resources
|
|
200
|
+
*/
|
|
201
|
+
async dispose() {
|
|
202
|
+
if (this.tesseract) {
|
|
203
|
+
// await this.tesseract.terminate();
|
|
204
|
+
this.tesseract = null;
|
|
205
|
+
}
|
|
206
|
+
this.isInitialized = false;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
exports.OCREngine = OCREngine;
|
|
210
|
+
/**
|
|
211
|
+
* Clean and normalize extracted text
|
|
212
|
+
*/
|
|
213
|
+
function cleanText(text) {
|
|
214
|
+
return text
|
|
215
|
+
.replace(/[^\w\s\-\/\.\:]/g, '') // Remove special characters
|
|
216
|
+
.replace(/\s+/g, ' ') // Normalize whitespace
|
|
217
|
+
.trim();
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Validate date format
|
|
221
|
+
*/
|
|
222
|
+
function isValidDate(dateString) {
|
|
223
|
+
const date = new Date(dateString);
|
|
224
|
+
return date instanceof Date && !isNaN(date.getTime());
|
|
225
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@hexar/biometric-identity-sdk-core",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Core AI engine for biometric identity verification",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"private": false,
|
|
8
|
+
"scripts": {
|
|
9
|
+
"build": "tsc",
|
|
10
|
+
"dev": "tsc --watch",
|
|
11
|
+
"clean": "rm -rf dist"
|
|
12
|
+
},
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"onnxruntime-node": "^1.17.0",
|
|
15
|
+
"onnxruntime-web": "^1.17.0",
|
|
16
|
+
"react-native-quick-crypto": "^1.0.3",
|
|
17
|
+
"sharp": "^0.33.2",
|
|
18
|
+
"tesseract.js": "^5.0.4"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"@types/node": "^20.11.19",
|
|
22
|
+
"typescript": "^5.3.3"
|
|
23
|
+
}
|
|
24
|
+
}
|