id-scanner-lib 1.3.3 → 1.5.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/README.md +55 -460
- package/dist/id-scanner-lib.esm.js +4641 -0
- package/dist/id-scanner-lib.esm.js.map +1 -0
- package/dist/id-scanner-lib.js +14755 -0
- package/dist/id-scanner-lib.js.map +1 -0
- package/dist/types/core/base-module.d.ts +44 -0
- package/dist/types/core/camera-manager.d.ts +258 -0
- package/dist/types/core/config.d.ts +88 -0
- package/dist/types/core/errors.d.ts +111 -0
- package/dist/types/core/event-emitter.d.ts +55 -0
- package/dist/types/core/logger.d.ts +277 -0
- package/dist/types/core/module-manager.d.ts +78 -0
- package/dist/types/core/plugin-manager.d.ts +158 -0
- package/dist/types/core/resource-manager.d.ts +246 -0
- package/dist/types/core/result.d.ts +83 -0
- package/dist/types/core/scanner-factory.d.ts +93 -0
- package/dist/types/index.bundle.d.ts +1303 -0
- package/dist/types/index.d.ts +86 -0
- package/dist/types/interfaces/external-types.d.ts +174 -0
- package/dist/types/interfaces/face-detection.d.ts +293 -0
- package/dist/types/interfaces/scanner-module.d.ts +280 -0
- package/dist/types/modules/face/face-detector.d.ts +170 -0
- package/dist/types/modules/face/index.d.ts +56 -0
- package/dist/types/modules/face/liveness-detector.d.ts +177 -0
- package/dist/types/modules/face/types.d.ts +136 -0
- package/dist/types/modules/id-card/anti-fake-detector.d.ts +170 -0
- package/dist/types/modules/id-card/id-card-detector.d.ts +131 -0
- package/dist/types/modules/id-card/index.d.ts +89 -0
- package/dist/types/modules/id-card/ocr-processor.d.ts +110 -0
- package/dist/types/modules/id-card/ocr-worker.d.ts +31 -0
- package/dist/types/modules/id-card/types.d.ts +181 -0
- package/dist/types/modules/qrcode/index.d.ts +51 -0
- package/dist/types/modules/qrcode/qr-code-scanner.d.ts +64 -0
- package/dist/types/modules/qrcode/types.d.ts +67 -0
- package/dist/types/utils/camera.d.ts +81 -0
- package/dist/types/utils/image-processing.d.ts +176 -0
- package/dist/types/utils/index.d.ts +175 -0
- package/dist/types/utils/performance.d.ts +81 -0
- package/dist/types/utils/resource-manager.d.ts +53 -0
- package/dist/types/utils/types.d.ts +166 -0
- package/dist/types/utils/worker.d.ts +52 -0
- package/dist/types/version.d.ts +7 -0
- package/package.json +76 -75
- package/src/core/base-module.ts +78 -0
- package/src/core/camera-manager.ts +798 -0
- package/src/core/config.ts +268 -0
- package/src/core/errors.ts +174 -0
- package/src/core/event-emitter.ts +110 -0
- package/src/core/logger.ts +549 -0
- package/src/core/module-manager.ts +165 -0
- package/src/core/plugin-manager.ts +429 -0
- package/src/core/resource-manager.ts +762 -0
- package/src/core/result.ts +163 -0
- package/src/core/scanner-factory.ts +237 -0
- package/src/index.ts +113 -936
- package/src/interfaces/external-types.ts +200 -0
- package/src/interfaces/face-detection.ts +309 -0
- package/src/interfaces/scanner-module.ts +384 -0
- package/src/modules/face/face-detector.ts +931 -0
- package/src/modules/face/index.ts +208 -0
- package/src/modules/face/liveness-detector.ts +908 -0
- package/src/modules/face/types.ts +133 -0
- package/src/{id-recognition → modules/id-card}/anti-fake-detector.ts +273 -239
- package/src/modules/id-card/id-card-detector.ts +474 -0
- package/src/modules/id-card/index.ts +425 -0
- package/src/{id-recognition → modules/id-card}/ocr-processor.ts +149 -92
- package/src/modules/id-card/ocr-worker.ts +259 -0
- package/src/modules/id-card/types.ts +178 -0
- package/src/modules/qrcode/index.ts +175 -0
- package/src/modules/qrcode/qr-code-scanner.ts +230 -0
- package/src/modules/qrcode/types.ts +65 -0
- package/src/types/tesseract.d.ts +265 -22
- package/src/utils/image-processing.ts +68 -49
- package/src/utils/index.ts +426 -0
- package/src/utils/performance.ts +168 -131
- package/src/utils/resource-manager.ts +65 -146
- package/src/utils/types.ts +90 -2
- package/src/utils/worker.ts +123 -84
- package/src/version.ts +11 -0
- package/tools/scaffold.js +543 -0
- package/dist/id-scanner-core.esm.js +0 -11349
- package/dist/id-scanner-core.js +0 -11361
- package/dist/id-scanner-core.min.js +0 -1
- package/dist/id-scanner-ocr.esm.js +0 -2319
- package/dist/id-scanner-ocr.js +0 -2328
- package/dist/id-scanner-ocr.min.js +0 -1
- package/dist/id-scanner-qr.esm.js +0 -1296
- package/dist/id-scanner-qr.js +0 -1305
- package/dist/id-scanner-qr.min.js +0 -1
- package/dist/id-scanner.js +0 -4561
- package/dist/id-scanner.min.js +0 -1
- package/src/core.ts +0 -138
- package/src/demo/demo.ts +0 -204
- package/src/id-recognition/data-extractor.ts +0 -262
- package/src/id-recognition/id-detector.ts +0 -510
- package/src/id-recognition/ocr-worker.ts +0 -156
- package/src/index-umd.ts +0 -477
- package/src/ocr-module.ts +0 -187
- package/src/qr-module.ts +0 -179
- package/src/scanner/barcode-scanner.ts +0 -251
- package/src/scanner/qr-scanner.ts +0 -167
package/src/index-umd.ts
DELETED
|
@@ -1,477 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file ID扫描识别库UMD格式入口文件
|
|
3
|
-
* @description 专门为UMD格式构建的入口,使用静态导入而非动态导入
|
|
4
|
-
* @module IDScannerLib
|
|
5
|
-
* @version 1.1.0
|
|
6
|
-
* @license MIT
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { Camera, CameraOptions } from "./utils/camera"
|
|
10
|
-
import { IDCardInfo, DetectionResult } from "./utils/types"
|
|
11
|
-
import type { QRScannerOptions } from "./scanner/qr-scanner"
|
|
12
|
-
import type { BarcodeScannerOptions } from "./scanner/barcode-scanner"
|
|
13
|
-
|
|
14
|
-
// 静态导入所有依赖
|
|
15
|
-
import { QRScanner } from "./scanner/qr-scanner"
|
|
16
|
-
import { BarcodeScanner } from "./scanner/barcode-scanner"
|
|
17
|
-
import {
|
|
18
|
-
IDCardDetector,
|
|
19
|
-
IDCardDetectorOptions,
|
|
20
|
-
} from "./id-recognition/id-detector"
|
|
21
|
-
import { OCRProcessor } from "./id-recognition/ocr-processor"
|
|
22
|
-
import { DataExtractor } from "./id-recognition/data-extractor"
|
|
23
|
-
import { ImageProcessor } from "./utils/image-processing"
|
|
24
|
-
// 导入IDScannerDemo
|
|
25
|
-
import { IDScannerDemo } from "./demo/demo"
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* IDScanner配置选项接口
|
|
29
|
-
*/
|
|
30
|
-
export interface IDScannerOptions {
|
|
31
|
-
cameraOptions?: CameraOptions
|
|
32
|
-
qrScannerOptions?: QRScannerOptions
|
|
33
|
-
barcodeScannerOptions?: BarcodeScannerOptions
|
|
34
|
-
onQRCodeScanned?: (result: string) => void
|
|
35
|
-
onBarcodeScanned?: (result: string) => void
|
|
36
|
-
onIDCardScanned?: (info: IDCardInfo) => void
|
|
37
|
-
onError?: (error: Error) => void
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* IDScanner 主类
|
|
42
|
-
* UMD版本使用静态导入实现
|
|
43
|
-
*/
|
|
44
|
-
export class IDScanner {
|
|
45
|
-
private camera: Camera
|
|
46
|
-
private qrScanner: QRScanner | null = null
|
|
47
|
-
private barcodeScanner: BarcodeScanner | null = null
|
|
48
|
-
private idDetector: IDCardDetector | null = null
|
|
49
|
-
private ocrProcessor: OCRProcessor | null = null
|
|
50
|
-
private dataExtractor: DataExtractor | null = null
|
|
51
|
-
private scanMode: "qr" | "barcode" | "idcard" = "qr"
|
|
52
|
-
private videoElement: HTMLVideoElement | null = null
|
|
53
|
-
|
|
54
|
-
// 添加静态属性IDScannerDemo,使其能被通过IDScanner.IDScannerDemo访问
|
|
55
|
-
static IDScannerDemo = IDScannerDemo
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* 构造函数
|
|
59
|
-
* @param options 配置选项
|
|
60
|
-
*/
|
|
61
|
-
constructor(private options: IDScannerOptions = {}) {
|
|
62
|
-
this.camera = new Camera(options.cameraOptions)
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* 初始化模块
|
|
67
|
-
* 根据需要初始化OCR引擎
|
|
68
|
-
*/
|
|
69
|
-
async initialize(): Promise<void> {
|
|
70
|
-
try {
|
|
71
|
-
// 初始化OCR模块
|
|
72
|
-
this.ocrProcessor = new OCRProcessor()
|
|
73
|
-
this.dataExtractor = new DataExtractor()
|
|
74
|
-
await this.ocrProcessor.initialize()
|
|
75
|
-
|
|
76
|
-
console.log("IDScanner initialized")
|
|
77
|
-
} catch (error) {
|
|
78
|
-
this.handleError(error as Error)
|
|
79
|
-
throw error
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* 启动二维码扫描
|
|
85
|
-
* @param videoElement HTML视频元素
|
|
86
|
-
*/
|
|
87
|
-
async startQRScanner(videoElement: HTMLVideoElement): Promise<void> {
|
|
88
|
-
this.stop()
|
|
89
|
-
this.videoElement = videoElement
|
|
90
|
-
this.scanMode = "qr"
|
|
91
|
-
|
|
92
|
-
try {
|
|
93
|
-
if (!this.qrScanner) {
|
|
94
|
-
this.qrScanner = new QRScanner({
|
|
95
|
-
...this.options.qrScannerOptions,
|
|
96
|
-
onScan: this.handleQRScan.bind(this),
|
|
97
|
-
})
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
await this.camera.start(videoElement)
|
|
101
|
-
this.qrScanner.start(videoElement)
|
|
102
|
-
} catch (error) {
|
|
103
|
-
this.handleError(error as Error)
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* 启动条形码扫描
|
|
109
|
-
* @param videoElement HTML视频元素
|
|
110
|
-
*/
|
|
111
|
-
async startBarcodeScanner(videoElement: HTMLVideoElement): Promise<void> {
|
|
112
|
-
this.stop()
|
|
113
|
-
this.videoElement = videoElement
|
|
114
|
-
this.scanMode = "barcode"
|
|
115
|
-
|
|
116
|
-
try {
|
|
117
|
-
if (!this.barcodeScanner) {
|
|
118
|
-
this.barcodeScanner = new BarcodeScanner({
|
|
119
|
-
...this.options.barcodeScannerOptions,
|
|
120
|
-
onScan: this.handleBarcodeScan.bind(this),
|
|
121
|
-
})
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
await this.camera.start(videoElement)
|
|
125
|
-
this.barcodeScanner.start(videoElement)
|
|
126
|
-
} catch (error) {
|
|
127
|
-
this.handleError(error as Error)
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* 启动身份证扫描
|
|
133
|
-
* @param videoElement HTML视频元素
|
|
134
|
-
*/
|
|
135
|
-
async startIDCardScanner(videoElement: HTMLVideoElement): Promise<void> {
|
|
136
|
-
this.stop()
|
|
137
|
-
this.videoElement = videoElement
|
|
138
|
-
this.scanMode = "idcard"
|
|
139
|
-
|
|
140
|
-
try {
|
|
141
|
-
if (!this.ocrProcessor) {
|
|
142
|
-
await this.initialize()
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
if (!this.idDetector) {
|
|
146
|
-
this.idDetector = new IDCardDetector({
|
|
147
|
-
onDetection: this.handleIDDetection.bind(this),
|
|
148
|
-
onError: this.handleError.bind(this),
|
|
149
|
-
} as IDCardDetectorOptions)
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
await this.camera.start(videoElement)
|
|
153
|
-
this.idDetector.start(videoElement)
|
|
154
|
-
} catch (error) {
|
|
155
|
-
this.handleError(error as Error)
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* 停止扫描
|
|
161
|
-
*/
|
|
162
|
-
stop(): void {
|
|
163
|
-
if (this.scanMode === "qr" && this.qrScanner) {
|
|
164
|
-
this.qrScanner.stop()
|
|
165
|
-
} else if (this.scanMode === "barcode" && this.barcodeScanner) {
|
|
166
|
-
this.barcodeScanner.stop()
|
|
167
|
-
} else if (this.scanMode === "idcard" && this.idDetector) {
|
|
168
|
-
this.idDetector.stop()
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
this.camera.stop()
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
/**
|
|
175
|
-
* 处理二维码扫描结果
|
|
176
|
-
*/
|
|
177
|
-
private handleQRScan(result: string): void {
|
|
178
|
-
if (this.options.onQRCodeScanned) {
|
|
179
|
-
this.options.onQRCodeScanned(result)
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
/**
|
|
184
|
-
* 处理条形码扫描结果
|
|
185
|
-
*/
|
|
186
|
-
private handleBarcodeScan(result: string): void {
|
|
187
|
-
if (this.options.onBarcodeScanned) {
|
|
188
|
-
this.options.onBarcodeScanned(result)
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
/**
|
|
193
|
-
* 处理身份证检测结果
|
|
194
|
-
*/
|
|
195
|
-
private async handleIDDetection(result: DetectionResult): Promise<void> {
|
|
196
|
-
if (!this.ocrProcessor || !this.dataExtractor) return
|
|
197
|
-
|
|
198
|
-
try {
|
|
199
|
-
// 检查 imageData 是否存在
|
|
200
|
-
if (!result.imageData) {
|
|
201
|
-
this.handleError(new Error("无效的图像数据"))
|
|
202
|
-
return
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
const idCardInfo = await this.ocrProcessor.processIDCard(result.imageData)
|
|
206
|
-
const extractedInfo = this.dataExtractor.extractAndValidate(idCardInfo)
|
|
207
|
-
|
|
208
|
-
if (this.options.onIDCardScanned) {
|
|
209
|
-
this.options.onIDCardScanned(extractedInfo)
|
|
210
|
-
}
|
|
211
|
-
} catch (error) {
|
|
212
|
-
this.handleError(error as Error)
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
/**
|
|
217
|
-
* 处理错误
|
|
218
|
-
*/
|
|
219
|
-
private handleError(error: Error): void {
|
|
220
|
-
if (this.options.onError) {
|
|
221
|
-
this.options.onError(error)
|
|
222
|
-
} else {
|
|
223
|
-
console.error("IDScanner error:", error)
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
/**
|
|
228
|
-
* 释放资源
|
|
229
|
-
*/
|
|
230
|
-
async terminate(): Promise<void> {
|
|
231
|
-
this.stop()
|
|
232
|
-
|
|
233
|
-
if (this.ocrProcessor) {
|
|
234
|
-
await this.ocrProcessor.terminate()
|
|
235
|
-
this.ocrProcessor = null
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
this.qrScanner = null
|
|
239
|
-
this.barcodeScanner = null
|
|
240
|
-
this.idDetector = null
|
|
241
|
-
this.dataExtractor = null
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
/**
|
|
245
|
-
* 处理图片中的二维码
|
|
246
|
-
* @param imageSource 图片源,可以是Image元素、Canvas元素或URL字符串
|
|
247
|
-
* @returns 返回Promise,解析为扫描结果
|
|
248
|
-
*/
|
|
249
|
-
async processQRCodeImage(
|
|
250
|
-
imageSource: HTMLImageElement | HTMLCanvasElement | string
|
|
251
|
-
): Promise<string> {
|
|
252
|
-
try {
|
|
253
|
-
if (!this.qrScanner) {
|
|
254
|
-
this.qrScanner = new QRScanner({
|
|
255
|
-
...this.options.qrScannerOptions,
|
|
256
|
-
onScan: this.handleQRScan.bind(this),
|
|
257
|
-
})
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
// 处理不同类型的图片源
|
|
261
|
-
let imageElement: HTMLImageElement
|
|
262
|
-
if (typeof imageSource === "string") {
|
|
263
|
-
// 如果是URL字符串,创建新的Image元素并加载图片
|
|
264
|
-
imageElement = new Image()
|
|
265
|
-
imageElement.crossOrigin = "anonymous" // 处理跨域图片
|
|
266
|
-
await new Promise((resolve, reject) => {
|
|
267
|
-
imageElement.onload = resolve
|
|
268
|
-
imageElement.onerror = reject
|
|
269
|
-
imageElement.src = imageSource
|
|
270
|
-
})
|
|
271
|
-
} else if (imageSource instanceof HTMLImageElement) {
|
|
272
|
-
// 如果已经是Image元素,直接使用
|
|
273
|
-
imageElement = imageSource
|
|
274
|
-
} else if (imageSource instanceof HTMLCanvasElement) {
|
|
275
|
-
// 如果是Canvas元素,创建新的Image元素并从Canvas获取数据
|
|
276
|
-
const dataURL = imageSource.toDataURL()
|
|
277
|
-
imageElement = new Image()
|
|
278
|
-
await new Promise((resolve, reject) => {
|
|
279
|
-
imageElement.onload = resolve
|
|
280
|
-
imageElement.onerror = reject
|
|
281
|
-
imageElement.src = dataURL
|
|
282
|
-
})
|
|
283
|
-
} else {
|
|
284
|
-
throw new Error("不支持的图片源类型")
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
// 创建Canvas处理图片
|
|
288
|
-
const canvas = document.createElement("canvas")
|
|
289
|
-
const ctx = canvas.getContext("2d")
|
|
290
|
-
if (!ctx) {
|
|
291
|
-
throw new Error("无法创建Canvas上下文")
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
// 设置Canvas尺寸与图片相同
|
|
295
|
-
canvas.width = imageElement.naturalWidth
|
|
296
|
-
canvas.height = imageElement.naturalHeight
|
|
297
|
-
ctx.drawImage(imageElement, 0, 0)
|
|
298
|
-
|
|
299
|
-
// 获取图像数据并处理
|
|
300
|
-
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height)
|
|
301
|
-
return new Promise((resolve, reject) => {
|
|
302
|
-
try {
|
|
303
|
-
const result = this.qrScanner?.processImageData(imageData)
|
|
304
|
-
if (result) {
|
|
305
|
-
resolve(result)
|
|
306
|
-
} else {
|
|
307
|
-
reject(new Error("未检测到二维码"))
|
|
308
|
-
}
|
|
309
|
-
} catch (error) {
|
|
310
|
-
reject(error)
|
|
311
|
-
}
|
|
312
|
-
})
|
|
313
|
-
} catch (error) {
|
|
314
|
-
this.handleError(error as Error)
|
|
315
|
-
throw error
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
/**
|
|
320
|
-
* 处理图片中的条形码
|
|
321
|
-
* @param imageSource 图片源,可以是Image元素、Canvas元素或URL字符串
|
|
322
|
-
* @returns 返回Promise,解析为扫描结果
|
|
323
|
-
*/
|
|
324
|
-
async processBarcodeImage(
|
|
325
|
-
imageSource: HTMLImageElement | HTMLCanvasElement | string
|
|
326
|
-
): Promise<string> {
|
|
327
|
-
try {
|
|
328
|
-
if (!this.barcodeScanner) {
|
|
329
|
-
this.barcodeScanner = new BarcodeScanner({
|
|
330
|
-
...this.options.barcodeScannerOptions,
|
|
331
|
-
onScan: this.handleBarcodeScan.bind(this),
|
|
332
|
-
})
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
// 处理不同类型的图片源
|
|
336
|
-
let imageElement: HTMLImageElement
|
|
337
|
-
if (typeof imageSource === "string") {
|
|
338
|
-
// 如果是URL字符串,创建新的Image元素并加载图片
|
|
339
|
-
imageElement = new Image()
|
|
340
|
-
imageElement.crossOrigin = "anonymous" // 处理跨域图片
|
|
341
|
-
await new Promise((resolve, reject) => {
|
|
342
|
-
imageElement.onload = resolve
|
|
343
|
-
imageElement.onerror = reject
|
|
344
|
-
imageElement.src = imageSource
|
|
345
|
-
})
|
|
346
|
-
} else if (imageSource instanceof HTMLImageElement) {
|
|
347
|
-
// 如果已经是Image元素,直接使用
|
|
348
|
-
imageElement = imageSource
|
|
349
|
-
} else if (imageSource instanceof HTMLCanvasElement) {
|
|
350
|
-
// 如果是Canvas元素,创建新的Image元素并从Canvas获取数据
|
|
351
|
-
const dataURL = imageSource.toDataURL()
|
|
352
|
-
imageElement = new Image()
|
|
353
|
-
await new Promise((resolve, reject) => {
|
|
354
|
-
imageElement.onload = resolve
|
|
355
|
-
imageElement.onerror = reject
|
|
356
|
-
imageElement.src = dataURL
|
|
357
|
-
})
|
|
358
|
-
} else {
|
|
359
|
-
throw new Error("不支持的图片源类型")
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
// 创建Canvas处理图片
|
|
363
|
-
const canvas = document.createElement("canvas")
|
|
364
|
-
const ctx = canvas.getContext("2d")
|
|
365
|
-
if (!ctx) {
|
|
366
|
-
throw new Error("无法创建Canvas上下文")
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
// 设置Canvas尺寸与图片相同
|
|
370
|
-
canvas.width = imageElement.naturalWidth
|
|
371
|
-
canvas.height = imageElement.naturalHeight
|
|
372
|
-
ctx.drawImage(imageElement, 0, 0)
|
|
373
|
-
|
|
374
|
-
// 获取图像数据并处理
|
|
375
|
-
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height)
|
|
376
|
-
return new Promise((resolve, reject) => {
|
|
377
|
-
try {
|
|
378
|
-
const result = this.barcodeScanner?.processImageData(imageData)
|
|
379
|
-
if (result) {
|
|
380
|
-
resolve(result)
|
|
381
|
-
} else {
|
|
382
|
-
reject(new Error("未检测到条形码"))
|
|
383
|
-
}
|
|
384
|
-
} catch (error) {
|
|
385
|
-
reject(error)
|
|
386
|
-
}
|
|
387
|
-
})
|
|
388
|
-
} catch (error) {
|
|
389
|
-
this.handleError(error as Error)
|
|
390
|
-
throw error
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
/**
|
|
395
|
-
* 处理图片中的身份证
|
|
396
|
-
* @param imageSource 图片源,可以是Image元素、Canvas元素或URL字符串
|
|
397
|
-
* @returns 返回Promise,解析为身份证信息
|
|
398
|
-
*/
|
|
399
|
-
async processIDCardImage(
|
|
400
|
-
imageSource: HTMLImageElement | HTMLCanvasElement | string
|
|
401
|
-
): Promise<IDCardInfo> {
|
|
402
|
-
try {
|
|
403
|
-
if (!this.ocrProcessor || !this.dataExtractor) {
|
|
404
|
-
await this.initialize()
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
// 处理不同类型的图片源
|
|
408
|
-
let imageElement: HTMLImageElement
|
|
409
|
-
if (typeof imageSource === "string") {
|
|
410
|
-
// 如果是URL字符串,创建新的Image元素并加载图片
|
|
411
|
-
imageElement = new Image()
|
|
412
|
-
imageElement.crossOrigin = "anonymous" // 处理跨域图片
|
|
413
|
-
await new Promise((resolve, reject) => {
|
|
414
|
-
imageElement.onload = resolve
|
|
415
|
-
imageElement.onerror = reject
|
|
416
|
-
imageElement.src = imageSource
|
|
417
|
-
})
|
|
418
|
-
} else if (imageSource instanceof HTMLImageElement) {
|
|
419
|
-
// 如果已经是Image元素,直接使用
|
|
420
|
-
imageElement = imageSource
|
|
421
|
-
} else if (imageSource instanceof HTMLCanvasElement) {
|
|
422
|
-
// 如果是Canvas元素,创建新的Image元素并从Canvas获取数据
|
|
423
|
-
const dataURL = imageSource.toDataURL()
|
|
424
|
-
imageElement = new Image()
|
|
425
|
-
await new Promise((resolve, reject) => {
|
|
426
|
-
imageElement.onload = resolve
|
|
427
|
-
imageElement.onerror = reject
|
|
428
|
-
imageElement.src = dataURL
|
|
429
|
-
})
|
|
430
|
-
} else {
|
|
431
|
-
throw new Error("不支持的图片源类型")
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
// 创建Canvas处理图片
|
|
435
|
-
const canvas = document.createElement("canvas")
|
|
436
|
-
const ctx = canvas.getContext("2d")
|
|
437
|
-
if (!ctx) {
|
|
438
|
-
throw new Error("无法创建Canvas上下文")
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
// 设置Canvas尺寸与图片相同
|
|
442
|
-
canvas.width = imageElement.naturalWidth
|
|
443
|
-
canvas.height = imageElement.naturalHeight
|
|
444
|
-
ctx.drawImage(imageElement, 0, 0)
|
|
445
|
-
|
|
446
|
-
// 获取图像数据并处理
|
|
447
|
-
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height)
|
|
448
|
-
|
|
449
|
-
// 调用OCR处理器处理身份证图像
|
|
450
|
-
const ocrResult = await this.ocrProcessor!.processIDCard(imageData)
|
|
451
|
-
const extractedInfo = this.dataExtractor!.extractAndValidate(ocrResult)
|
|
452
|
-
|
|
453
|
-
if (this.options.onIDCardScanned) {
|
|
454
|
-
this.options.onIDCardScanned(extractedInfo)
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
return extractedInfo
|
|
458
|
-
} catch (error) {
|
|
459
|
-
this.handleError(error as Error)
|
|
460
|
-
throw error
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
// 导出核心类型
|
|
466
|
-
export { IDCardInfo } from "./utils/types"
|
|
467
|
-
export { CameraOptions } from "./utils/camera"
|
|
468
|
-
export {
|
|
469
|
-
QRScanner,
|
|
470
|
-
BarcodeScanner,
|
|
471
|
-
IDCardDetector,
|
|
472
|
-
OCRProcessor,
|
|
473
|
-
DataExtractor,
|
|
474
|
-
ImageProcessor,
|
|
475
|
-
}
|
|
476
|
-
// 导出IDScannerDemo类
|
|
477
|
-
export { IDScannerDemo }
|
package/src/ocr-module.ts
DELETED
|
@@ -1,187 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file OCR模块入口文件
|
|
3
|
-
* @description 包含身份证OCR识别相关功能
|
|
4
|
-
* @module IDScannerOCR
|
|
5
|
-
* @version 1.0.0
|
|
6
|
-
* @license MIT
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { Camera, CameraOptions } from "./utils/camera"
|
|
10
|
-
import { ImageProcessor } from "./utils/image-processing"
|
|
11
|
-
import { IDCardInfo, DetectionResult } from "./utils/types"
|
|
12
|
-
import {
|
|
13
|
-
IDCardDetector,
|
|
14
|
-
IDCardDetectorOptions,
|
|
15
|
-
} from "./id-recognition/id-detector"
|
|
16
|
-
import { OCRProcessor } from "./id-recognition/ocr-processor"
|
|
17
|
-
import { DataExtractor } from "./id-recognition/data-extractor"
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* OCR模块配置选项
|
|
21
|
-
*/
|
|
22
|
-
export interface OCRModuleOptions {
|
|
23
|
-
cameraOptions?: CameraOptions
|
|
24
|
-
onIDCardScanned?: (info: IDCardInfo) => void
|
|
25
|
-
onError?: (error: Error) => void
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* OCR模块类
|
|
30
|
-
*
|
|
31
|
-
* 提供身份证检测和OCR文字识别功能
|
|
32
|
-
*/
|
|
33
|
-
export class OCRModule {
|
|
34
|
-
private idDetector: IDCardDetector
|
|
35
|
-
private ocrProcessor: OCRProcessor
|
|
36
|
-
private dataExtractor: DataExtractor
|
|
37
|
-
private camera: Camera
|
|
38
|
-
private isRunning: boolean = false
|
|
39
|
-
private videoElement: HTMLVideoElement | null = null
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* 构造函数
|
|
43
|
-
* @param options 配置选项
|
|
44
|
-
*/
|
|
45
|
-
constructor(private options: OCRModuleOptions = {}) {
|
|
46
|
-
this.camera = new Camera(options.cameraOptions)
|
|
47
|
-
this.idDetector = new IDCardDetector({
|
|
48
|
-
onDetection: this.handleIDDetection.bind(this),
|
|
49
|
-
onError: this.handleError.bind(this),
|
|
50
|
-
} as IDCardDetectorOptions)
|
|
51
|
-
this.ocrProcessor = new OCRProcessor()
|
|
52
|
-
this.dataExtractor = new DataExtractor()
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* 初始化OCR引擎
|
|
57
|
-
*
|
|
58
|
-
* @returns Promise<void>
|
|
59
|
-
*/
|
|
60
|
-
async initialize(): Promise<void> {
|
|
61
|
-
try {
|
|
62
|
-
await this.ocrProcessor.initialize()
|
|
63
|
-
console.log("OCR engine initialized")
|
|
64
|
-
} catch (error) {
|
|
65
|
-
this.handleError(error as Error)
|
|
66
|
-
throw error
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* 启动身份证扫描
|
|
72
|
-
* @param videoElement HTML视频元素
|
|
73
|
-
*/
|
|
74
|
-
async startIDCardScanner(videoElement: HTMLVideoElement): Promise<void> {
|
|
75
|
-
if (!this.ocrProcessor) {
|
|
76
|
-
throw new Error("OCR engine not initialized. Call initialize() first.")
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
this.videoElement = videoElement
|
|
80
|
-
this.isRunning = true
|
|
81
|
-
await this.camera.start(videoElement)
|
|
82
|
-
this.idDetector.start(videoElement)
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* 停止扫描
|
|
87
|
-
*/
|
|
88
|
-
stop(): void {
|
|
89
|
-
this.isRunning = false
|
|
90
|
-
this.idDetector.stop()
|
|
91
|
-
this.camera.stop()
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* 处理身份证检测结果
|
|
96
|
-
*/
|
|
97
|
-
private async handleIDDetection(result: DetectionResult): Promise<void> {
|
|
98
|
-
if (!this.isRunning) return
|
|
99
|
-
|
|
100
|
-
try {
|
|
101
|
-
// 检查 imageData 是否存在
|
|
102
|
-
if (!result.imageData) {
|
|
103
|
-
this.handleError(new Error("无效的图像数据"))
|
|
104
|
-
return
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
const idCardInfo = await this.ocrProcessor.processIDCard(result.imageData)
|
|
108
|
-
const extractedInfo = this.dataExtractor.extractAndValidate(idCardInfo)
|
|
109
|
-
|
|
110
|
-
if (this.options.onIDCardScanned) {
|
|
111
|
-
this.options.onIDCardScanned(extractedInfo)
|
|
112
|
-
}
|
|
113
|
-
} catch (error) {
|
|
114
|
-
this.handleError(error as Error)
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* 处理错误
|
|
120
|
-
*/
|
|
121
|
-
private handleError(error: Error): void {
|
|
122
|
-
if (this.options.onError) {
|
|
123
|
-
this.options.onError(error)
|
|
124
|
-
} else {
|
|
125
|
-
console.error("OCRModule error:", error)
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* 释放资源
|
|
131
|
-
*/
|
|
132
|
-
async terminate(): Promise<void> {
|
|
133
|
-
this.stop()
|
|
134
|
-
await this.ocrProcessor.terminate()
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* 直接处理图像数据中的身份证
|
|
139
|
-
* @param imageData 要处理的图像数据
|
|
140
|
-
* @returns 返回Promise,解析为身份证信息
|
|
141
|
-
*/
|
|
142
|
-
async processIDCard(imageData: ImageData): Promise<IDCardInfo> {
|
|
143
|
-
try {
|
|
144
|
-
if (!this.ocrProcessor) {
|
|
145
|
-
throw new Error("OCR engine not initialized. Call initialize() first.")
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// 检查图像数据有效性
|
|
149
|
-
if (
|
|
150
|
-
!imageData ||
|
|
151
|
-
!imageData.data ||
|
|
152
|
-
imageData.width <= 0 ||
|
|
153
|
-
imageData.height <= 0
|
|
154
|
-
) {
|
|
155
|
-
throw new Error("无效的图像数据")
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// 进行图像预处理,提高识别率
|
|
159
|
-
const processedImage = ImageProcessor.adjustBrightnessContrast(
|
|
160
|
-
imageData,
|
|
161
|
-
5, // 轻微提高亮度
|
|
162
|
-
10 // 适度提高对比度
|
|
163
|
-
)
|
|
164
|
-
|
|
165
|
-
// 调用OCR处理器进行文字识别
|
|
166
|
-
const idCardInfo = await this.ocrProcessor.processIDCard(processedImage)
|
|
167
|
-
// 提取和验证身份证信息
|
|
168
|
-
const extractedInfo = this.dataExtractor.extractAndValidate(idCardInfo)
|
|
169
|
-
|
|
170
|
-
// 如果有回调,触发回调
|
|
171
|
-
if (this.options.onIDCardScanned) {
|
|
172
|
-
this.options.onIDCardScanned(extractedInfo)
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
return extractedInfo
|
|
176
|
-
} catch (error) {
|
|
177
|
-
this.handleError(error as Error)
|
|
178
|
-
throw error
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
// 导出相关类型和工具
|
|
184
|
-
export { IDCardDetector } from "./id-recognition/id-detector"
|
|
185
|
-
export { OCRProcessor } from "./id-recognition/ocr-processor"
|
|
186
|
-
export { DataExtractor } from "./id-recognition/data-extractor"
|
|
187
|
-
export { IDCardInfo } from "./utils/types"
|