@jaak.ai/stamps 2.0.0-dev.41 → 2.0.0-dev.43

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.
Files changed (40) hide show
  1. package/dist/cjs/jaak-stamps-webcomponent.cjs.js +1 -1
  2. package/dist/cjs/jaak-stamps.cjs.entry.js +14 -652
  3. package/dist/cjs/jaak-stamps.cjs.entry.js.map +1 -1
  4. package/dist/cjs/jaak-stamps.entry.cjs.js.map +1 -1
  5. package/dist/cjs/loader.cjs.js +1 -1
  6. package/dist/collection/components/my-component/my-component.css +40 -0
  7. package/dist/collection/components/my-component/my-component.js +35 -343
  8. package/dist/collection/components/my-component/my-component.js.map +1 -1
  9. package/dist/collection/services/DetectionService.js +1 -164
  10. package/dist/collection/services/DetectionService.js.map +1 -1
  11. package/dist/collection/services/ServiceContainer.js +1 -6
  12. package/dist/collection/services/ServiceContainer.js.map +1 -1
  13. package/dist/collection/services/interfaces/IDetectionService.js.map +1 -1
  14. package/dist/collection/types/component-types.js +2 -0
  15. package/dist/collection/types/component-types.js.map +1 -0
  16. package/dist/components/jaak-stamps.js +15 -663
  17. package/dist/components/jaak-stamps.js.map +1 -1
  18. package/dist/esm/jaak-stamps-webcomponent.js +1 -1
  19. package/dist/esm/jaak-stamps.entry.js +14 -652
  20. package/dist/esm/jaak-stamps.entry.js.map +1 -1
  21. package/dist/esm/loader.js +1 -1
  22. package/dist/jaak-stamps-webcomponent/jaak-stamps-webcomponent.esm.js +1 -1
  23. package/dist/jaak-stamps-webcomponent/jaak-stamps.entry.esm.js.map +1 -1
  24. package/dist/jaak-stamps-webcomponent/p-3074405d.entry.js +2 -0
  25. package/dist/jaak-stamps-webcomponent/p-3074405d.entry.js.map +1 -0
  26. package/dist/types/components/my-component/my-component.d.ts +4 -45
  27. package/dist/types/components.d.ts +11 -62
  28. package/dist/types/services/DetectionService.d.ts +2 -22
  29. package/dist/types/services/ServiceContainer.d.ts +0 -6
  30. package/dist/types/services/interfaces/IDetectionService.d.ts +0 -13
  31. package/dist/types/types/component-types.d.ts +36 -0
  32. package/package.json +1 -1
  33. package/dist/collection/services/ImageQualityService.js +0 -329
  34. package/dist/collection/services/ImageQualityService.js.map +0 -1
  35. package/dist/collection/services/interfaces/IImageQualityService.js +0 -2
  36. package/dist/collection/services/interfaces/IImageQualityService.js.map +0 -1
  37. package/dist/jaak-stamps-webcomponent/p-47f37982.entry.js +0 -2
  38. package/dist/jaak-stamps-webcomponent/p-47f37982.entry.js.map +0 -1
  39. package/dist/types/services/ImageQualityService.d.ts +0 -31
  40. package/dist/types/services/interfaces/IImageQualityService.d.ts +0 -58
@@ -5,12 +5,12 @@
5
5
  * It contains typing information for all components that exist in this project.
6
6
  */
7
7
  import { HTMLStencilElement, JSXBase } from "./stencil-public-runtime";
8
- import { QualityThresholds } from "./services/interfaces/IDetectionService";
9
- export { QualityThresholds } from "./services/interfaces/IDetectionService";
8
+ import { CameraInfoResponse, CapturedImagesResponse, StatusResponse } from "./types/component-types";
9
+ export { CameraInfoResponse, CapturedImagesResponse, StatusResponse } from "./types/component-types";
10
10
  export namespace Components {
11
11
  interface JaakStamps {
12
12
  /**
13
- * @default 10
13
+ * @default 20
14
14
  */
15
15
  "alignmentTolerance": number;
16
16
  /**
@@ -18,58 +18,31 @@ export namespace Components {
18
18
  */
19
19
  "captureDelay": number;
20
20
  /**
21
- * @default 0
21
+ * @default 20
22
22
  */
23
23
  "cropMargin": number;
24
24
  /**
25
25
  * @default false
26
26
  */
27
27
  "debug": boolean;
28
- /**
29
- * @default true
30
- */
31
- "enableQualityValidation": boolean;
32
28
  "focusAtPoint": (x: number, y: number) => Promise<{ success: boolean; coordinates: { x: number; y: number; }; }>;
33
- "getCameraInfo": () => Promise<{ availableCameras: import("/home/runner/work/jaak-stamps-webcomponent/jaak-stamps-webcomponent/src/services/interfaces/ICameraService").CameraInfo[]; selectedCameraId: string | null; deviceType: string; isMultipleCamerasAvailable: boolean; preferredFacing: "environment" | "user" | null; }>;
29
+ "getCameraInfo": () => Promise<CameraInfoResponse>;
34
30
  "getCaptureDelay": () => Promise<number>;
35
- "getCapturedImages": () => Promise<import("/home/runner/work/jaak-stamps-webcomponent/jaak-stamps-webcomponent/src/services/interfaces/IStateManager").CapturedImages>;
36
- "getImageQuality": () => Promise<{ qualityScore: any; overallQuality: any; issues: any; recommendations: any; canCapture: boolean; }>;
37
- "getQualityThresholds": () => Promise<{ minQualityScore: number; minFocusScore: number; minBlurScore: number; maxReflectionScore: number; }>;
38
- "getStatus": () => Promise<{ isVideoActive: boolean; captureStep: "front" | "back" | "completed"; hasImages: boolean; isProcessCompleted: boolean; isModelPreloaded: boolean; }>;
31
+ "getCapturedImages": () => Promise<CapturedImagesResponse>;
32
+ "getStatus": () => Promise<StatusResponse>;
39
33
  "isProcessCompleted": () => Promise<boolean>;
40
34
  /**
41
- * @default 90
35
+ * @default 80
42
36
  */
43
37
  "maskSize": number;
44
- /**
45
- * @default 15
46
- */
47
- "maxReflectionScore": number;
48
- /**
49
- * @default 22
50
- */
51
- "minBlurScore": number;
52
- /**
53
- * @default 16
54
- */
55
- "minFocusScore": number;
56
- /**
57
- * @default 45
58
- */
59
- "minQualityScore": number;
60
38
  /**
61
39
  * @default 'auto'
62
40
  */
63
41
  "preferredCamera": 'auto' | 'front' | 'back';
64
42
  "preloadModel": () => Promise<{ success: boolean; message: string; error?: undefined; } | { success: boolean; error: any; message?: undefined; }>;
65
- /**
66
- * @default 60
67
- */
68
- "qualityThreshold": number;
69
43
  "resetCapture": () => Promise<void>;
70
44
  "setCaptureDelay": (delay: number) => Promise<{ success: boolean; captureDelay: number; }>;
71
45
  "setPreferredCamera": (camera: "auto" | "front" | "back") => Promise<{ success: boolean; selectedCamera: string; availableCameras: number; }>;
72
- "setQualityThresholds": (thresholds: QualityThresholds) => Promise<{ success: boolean; thresholds: QualityThresholds; }>;
73
46
  "setTorchEnabled": (enabled: boolean) => Promise<{ success: boolean; enabled: boolean; }>;
74
47
  "skipBackCapture": () => Promise<void>;
75
48
  "startCapture": () => Promise<void>;
@@ -110,7 +83,7 @@ declare global {
110
83
  declare namespace LocalJSX {
111
84
  interface JaakStamps {
112
85
  /**
113
- * @default 10
86
+ * @default 20
114
87
  */
115
88
  "alignmentTolerance"?: number;
116
89
  /**
@@ -118,7 +91,7 @@ declare namespace LocalJSX {
118
91
  */
119
92
  "captureDelay"?: number;
120
93
  /**
121
- * @default 0
94
+ * @default 20
122
95
  */
123
96
  "cropMargin"?: number;
124
97
  /**
@@ -126,39 +99,15 @@ declare namespace LocalJSX {
126
99
  */
127
100
  "debug"?: boolean;
128
101
  /**
129
- * @default true
130
- */
131
- "enableQualityValidation"?: boolean;
132
- /**
133
- * @default 90
102
+ * @default 80
134
103
  */
135
104
  "maskSize"?: number;
136
- /**
137
- * @default 15
138
- */
139
- "maxReflectionScore"?: number;
140
- /**
141
- * @default 22
142
- */
143
- "minBlurScore"?: number;
144
- /**
145
- * @default 16
146
- */
147
- "minFocusScore"?: number;
148
- /**
149
- * @default 45
150
- */
151
- "minQualityScore"?: number;
152
105
  "onCaptureCompleted"?: (event: JaakStampsCustomEvent<any>) => void;
153
106
  "onIsReady"?: (event: JaakStampsCustomEvent<boolean>) => void;
154
107
  /**
155
108
  * @default 'auto'
156
109
  */
157
110
  "preferredCamera"?: 'auto' | 'front' | 'back';
158
- /**
159
- * @default 60
160
- */
161
- "qualityThreshold"?: number;
162
111
  /**
163
112
  * @default false
164
113
  */
@@ -1,17 +1,14 @@
1
- import { IDetectionService, DetectionBox, SideAlignment, ClassificationResult, QualityThresholds } from './interfaces/IDetectionService';
1
+ import { IDetectionService, DetectionBox, SideAlignment, ClassificationResult } from './interfaces/IDetectionService';
2
2
  import { ILogger } from './interfaces/ILogger';
3
- import { ImageQualityResult } from './interfaces/IImageQualityService';
4
3
  export declare class DetectionService implements IDetectionService {
5
4
  private logger;
6
5
  private debug;
7
6
  private useDocumentClassification;
8
- private qualityThresholds;
9
7
  private session?;
10
8
  private mobileNetSession?;
11
9
  private mobileNetClassMap?;
12
10
  private modelLoaded;
13
11
  private deviceStrategy;
14
- private imageQualityService;
15
12
  private readonly MODEL_PATH;
16
13
  private readonly MOBILENET_MODEL_PATH;
17
14
  private readonly MOBILENET_CLASSES_PATH;
@@ -20,7 +17,7 @@ export declare class DetectionService implements IDetectionService {
20
17
  private preprocessCanvas?;
21
18
  private preprocessCtx?;
22
19
  private captureCanvas?;
23
- constructor(logger: ILogger, debug?: boolean, useDocumentClassification?: boolean, qualityThresholds?: QualityThresholds);
20
+ constructor(logger: ILogger, debug?: boolean, useDocumentClassification?: boolean);
24
21
  loadModel(): Promise<void>;
25
22
  loadClassificationModel(): Promise<void>;
26
23
  isModelLoaded(): boolean;
@@ -35,21 +32,4 @@ export declare class DetectionService implements IDetectionService {
35
32
  private cleanupCanvasPool;
36
33
  private float32ToFloat16;
37
34
  private preprocessMobileNet;
38
- validateImageQuality(video: HTMLVideoElement, detectedBox?: DetectionBox): ImageQualityResult;
39
- isImageQualityAcceptable(qualityResult: ImageQualityResult): boolean;
40
- updateQualityThresholds(thresholds: QualityThresholds): void;
41
- getQualityThresholds(): QualityThresholds;
42
- getQualityFeedback(qualityResult: ImageQualityResult): string;
43
- runInferenceWithQuality(inputTensor: any, video: HTMLVideoElement): Promise<{
44
- detections: DetectionBox[];
45
- qualityResult: ImageQualityResult;
46
- canCapture: boolean;
47
- feedback: string;
48
- }>;
49
- getQuickQualityFeedback(video: HTMLVideoElement): {
50
- hasBlur: boolean;
51
- hasReflections: boolean;
52
- isFocused: boolean;
53
- feedback: string;
54
- };
55
35
  }
@@ -11,12 +11,6 @@ export interface ComponentConfig {
11
11
  useDocumentClassification: boolean;
12
12
  preferredCamera: 'auto' | 'front' | 'back';
13
13
  captureDelay: number;
14
- enableQualityValidation?: boolean;
15
- qualityThreshold?: number;
16
- minQualityScore?: number;
17
- minFocusScore?: number;
18
- minBlurScore?: number;
19
- maxReflectionScore?: number;
20
14
  }
21
15
  export declare class ServiceContainer {
22
16
  private services;
@@ -17,12 +17,6 @@ export interface ClassificationResult {
17
17
  confidence: number;
18
18
  classIndex: number;
19
19
  }
20
- export interface QualityThresholds {
21
- minQualityScore?: number;
22
- minFocusScore?: number;
23
- minBlurScore?: number;
24
- maxReflectionScore?: number;
25
- }
26
20
  export interface IDetectionService {
27
21
  loadModel(): Promise<void>;
28
22
  loadClassificationModel(): Promise<void>;
@@ -33,12 +27,5 @@ export interface IDetectionService {
33
27
  checkSideAlignment(box: DetectionBox, maskConfig: any): SideAlignment;
34
28
  isCardInFrame(box: DetectionBox): boolean;
35
29
  areAllSidesAligned(alignment: SideAlignment): boolean;
36
- validateImageQuality(video: HTMLVideoElement, detectedBox?: DetectionBox): any;
37
- isImageQualityAcceptable(qualityResult: any): boolean;
38
- getQualityFeedback(qualityResult: any): string;
39
- runInferenceWithQuality(inputTensor: any, video: HTMLVideoElement): Promise<any>;
40
- getQuickQualityFeedback(video: HTMLVideoElement): any;
41
- updateQualityThresholds(thresholds: QualityThresholds): void;
42
- getQualityThresholds(): QualityThresholds;
43
30
  cleanup(): void;
44
31
  }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Type definitions for component methods to avoid absolute paths in components.d.ts
3
+ */
4
+ export interface CameraInfoResponse {
5
+ availableCameras: Array<{
6
+ id: string;
7
+ label: string;
8
+ selected: boolean;
9
+ }>;
10
+ selectedCameraId: string | null;
11
+ deviceType: string;
12
+ isMultipleCamerasAvailable: boolean;
13
+ preferredFacing: 'environment' | 'user' | null;
14
+ }
15
+ export interface CapturedImagesResponse {
16
+ front: {
17
+ fullFrame: string | null;
18
+ cropped: string | null;
19
+ };
20
+ back: {
21
+ fullFrame: string | null;
22
+ cropped: string | null;
23
+ };
24
+ metadata: {
25
+ totalImages: number;
26
+ processCompleted: boolean;
27
+ backCaptureSkipped: boolean;
28
+ };
29
+ }
30
+ export interface StatusResponse {
31
+ isVideoActive: boolean;
32
+ captureStep: 'front' | 'back' | 'completed';
33
+ hasImages: boolean;
34
+ isProcessCompleted: boolean;
35
+ isModelPreloaded: boolean;
36
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jaak.ai/stamps",
3
- "version": "2.0.0-dev.41",
3
+ "version": "2.0.0-dev.43",
4
4
  "description": "Jaak Document Identifier",
5
5
  "main": "dist/index.cjs.js",
6
6
  "module": "dist/index.js",
@@ -1,329 +0,0 @@
1
- export class ImageQualityService {
2
- logger;
3
- // Thresholds for quality assessment
4
- BLUR_THRESHOLD = 80; // Reduced from 100 to 80 for better acceptance
5
- FOCUS_THRESHOLD = 40; // Reduced from 50 to 40 for better acceptance
6
- REFLECTION_THRESHOLD = 240; // Pixel brightness threshold for reflections
7
- MIN_BRIGHTNESS = 50; // Minimum acceptable brightness
8
- MAX_BRIGHTNESS = 200; // Maximum acceptable brightness
9
- MIN_CONTRAST = 30; // Minimum acceptable contrast
10
- constructor(logger) {
11
- this.logger = logger;
12
- }
13
- analyzeImageQuality(canvas, documentBounds) {
14
- const ctx = canvas.getContext('2d');
15
- if (!ctx) {
16
- throw new Error('Unable to get canvas context');
17
- }
18
- // Get image data for the document area or full canvas
19
- let imageData;
20
- if (documentBounds) {
21
- imageData = ctx.getImageData(documentBounds.x, documentBounds.y, documentBounds.w, documentBounds.h);
22
- }
23
- else {
24
- imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
25
- }
26
- // Perform all quality assessments
27
- const blur = this.detectBlur(imageData);
28
- const reflection = this.detectReflections(imageData);
29
- const focus = this.measureFocus(imageData);
30
- const brightness = this.measureBrightness(imageData);
31
- const contrast = this.measureContrast(imageData);
32
- // Calculate overall quality score (0-100)
33
- const qualityScore = this.calculateOverallQuality(blur, reflection, focus, brightness, contrast);
34
- const overallQuality = this.getQualityLevel(qualityScore);
35
- // Identify issues and recommendations
36
- const issues = this.identifyIssues(blur, reflection, focus, brightness, contrast);
37
- const recommendations = this.getQualityRecommendations({
38
- blur, reflection, focus, brightness, contrast,
39
- overallQuality, qualityScore, issues, recommendations: []
40
- });
41
- const result = {
42
- blur,
43
- reflection,
44
- focus,
45
- brightness,
46
- contrast,
47
- overallQuality,
48
- qualityScore,
49
- issues,
50
- recommendations
51
- };
52
- this.logger.state('CALIDAD_IMAGEN_ANALIZADA', {
53
- qualityScore,
54
- overallQuality,
55
- issues: issues.length,
56
- hasBlur: !blur.isAcceptable,
57
- hasReflection: reflection.hasReflection,
58
- isFocused: focus.isFocused
59
- });
60
- return result;
61
- }
62
- detectBlur(imageData) {
63
- const { data, width, height } = imageData;
64
- let totalVariance = 0;
65
- let pixelCount = 0;
66
- // Calculate Laplacian variance for blur detection
67
- for (let y = 1; y < height - 1; y++) {
68
- for (let x = 1; x < width - 1; x++) {
69
- const idx = (y * width + x) * 4;
70
- // Convert to grayscale
71
- const gray = 0.299 * data[idx] + 0.587 * data[idx + 1] + 0.114 * data[idx + 2];
72
- // Calculate Laplacian (edge detection)
73
- const neighbors = [
74
- this.getGrayscale(data, x - 1, y - 1, width),
75
- this.getGrayscale(data, x, y - 1, width),
76
- this.getGrayscale(data, x + 1, y - 1, width),
77
- this.getGrayscale(data, x - 1, y, width),
78
- this.getGrayscale(data, x + 1, y, width),
79
- this.getGrayscale(data, x - 1, y + 1, width),
80
- this.getGrayscale(data, x, y + 1, width),
81
- this.getGrayscale(data, x + 1, y + 1, width)
82
- ];
83
- const laplacian = 8 * gray - neighbors.reduce((sum, val) => sum + val, 0);
84
- totalVariance += laplacian * laplacian;
85
- pixelCount++;
86
- }
87
- }
88
- const blurScore = Math.sqrt(totalVariance / pixelCount);
89
- const isAcceptable = blurScore > this.BLUR_THRESHOLD;
90
- return {
91
- blurScore,
92
- isAcceptable,
93
- threshold: this.BLUR_THRESHOLD
94
- };
95
- }
96
- detectReflections(imageData) {
97
- const { data, width, height } = imageData;
98
- let reflectionPixels = 0;
99
- const totalPixels = width * height;
100
- const reflectionAreas = [];
101
- // Track bright regions that could be reflections
102
- const brightRegions = Array(height).fill(null).map(() => Array(width).fill(false));
103
- for (let y = 0; y < height; y++) {
104
- for (let x = 0; x < width; x++) {
105
- const idx = (y * width + x) * 4;
106
- const brightness = (data[idx] + data[idx + 1] + data[idx + 2]) / 3;
107
- if (brightness > this.REFLECTION_THRESHOLD) {
108
- brightRegions[y][x] = true;
109
- reflectionPixels++;
110
- }
111
- }
112
- }
113
- // Find connected bright regions (potential reflections)
114
- const visited = Array(height).fill(null).map(() => Array(width).fill(false));
115
- for (let y = 0; y < height; y++) {
116
- for (let x = 0; x < width; x++) {
117
- if (brightRegions[y][x] && !visited[y][x]) {
118
- const region = this.findConnectedRegion(brightRegions, visited, x, y, width, height);
119
- if (region.size > 100) { // Minimum size for a reflection
120
- reflectionAreas.push({
121
- x: region.minX,
122
- y: region.minY,
123
- width: region.maxX - region.minX,
124
- height: region.maxY - region.minY
125
- });
126
- }
127
- }
128
- }
129
- }
130
- const reflectionScore = (reflectionPixels / totalPixels) * 100;
131
- const hasReflection = reflectionScore > 5 || reflectionAreas.length > 0; // 5% threshold
132
- return {
133
- reflectionScore,
134
- hasReflection,
135
- threshold: 5,
136
- reflectionAreas
137
- };
138
- }
139
- measureFocus(imageData) {
140
- const { data, width, height } = imageData;
141
- let edgeStrength = 0;
142
- let edgeCount = 0;
143
- // Sobel edge detection for focus measurement
144
- for (let y = 1; y < height - 1; y++) {
145
- for (let x = 1; x < width - 1; x++) {
146
- const gx = this.sobelX(data, x, y, width);
147
- const gy = this.sobelY(data, x, y, width);
148
- const magnitude = Math.sqrt(gx * gx + gy * gy);
149
- edgeStrength += magnitude;
150
- edgeCount++;
151
- }
152
- }
153
- const focusScore = edgeCount > 0 ? edgeStrength / edgeCount : 0;
154
- const isFocused = focusScore > this.FOCUS_THRESHOLD;
155
- return {
156
- focusScore,
157
- isFocused,
158
- threshold: this.FOCUS_THRESHOLD,
159
- edgeStrength
160
- };
161
- }
162
- measureBrightness(imageData) {
163
- const { data } = imageData;
164
- let totalBrightness = 0;
165
- const pixelCount = data.length / 4;
166
- for (let i = 0; i < data.length; i += 4) {
167
- const brightness = (data[i] + data[i + 1] + data[i + 2]) / 3;
168
- totalBrightness += brightness;
169
- }
170
- const brightness = totalBrightness / pixelCount;
171
- const isAcceptable = brightness >= this.MIN_BRIGHTNESS && brightness <= this.MAX_BRIGHTNESS;
172
- return {
173
- brightness,
174
- isAcceptable,
175
- minThreshold: this.MIN_BRIGHTNESS,
176
- maxThreshold: this.MAX_BRIGHTNESS
177
- };
178
- }
179
- measureContrast(imageData) {
180
- const { data } = imageData;
181
- let min = 255;
182
- let max = 0;
183
- for (let i = 0; i < data.length; i += 4) {
184
- const brightness = (data[i] + data[i + 1] + data[i + 2]) / 3;
185
- min = Math.min(min, brightness);
186
- max = Math.max(max, brightness);
187
- }
188
- const contrast = max - min;
189
- const isAcceptable = contrast > this.MIN_CONTRAST;
190
- return {
191
- contrast,
192
- isAcceptable,
193
- threshold: this.MIN_CONTRAST
194
- };
195
- }
196
- getQualityRecommendations(result) {
197
- const recommendations = [];
198
- if (!result.blur.isAcceptable) {
199
- recommendations.push('Mantenga el dispositivo estable para reducir el desenfoque');
200
- recommendations.push('Acérquese al documento para mejorar la nitidez');
201
- }
202
- if (result.reflection.hasReflection) {
203
- recommendations.push('Cambie el ángulo del dispositivo para evitar reflejos');
204
- recommendations.push('Mejore la iluminación indirecta del área');
205
- }
206
- if (!result.focus.isFocused) {
207
- recommendations.push('Permita que la cámara enfoque automáticamente');
208
- recommendations.push('Toque la pantalla sobre el documento para enfocar');
209
- }
210
- if (!result.brightness.isAcceptable) {
211
- if (result.brightness.brightness < result.brightness.minThreshold) {
212
- recommendations.push('Aumente la iluminación del área');
213
- recommendations.push('Muévase a un lugar con mejor luz');
214
- }
215
- else {
216
- recommendations.push('Reduzca la iluminación directa sobre el documento');
217
- recommendations.push('Evite la luz solar directa');
218
- }
219
- }
220
- if (!result.contrast.isAcceptable) {
221
- recommendations.push('Mejore la iluminación para aumentar el contraste');
222
- recommendations.push('Asegúrese de que el fondo contraste con el documento');
223
- }
224
- if (recommendations.length === 0) {
225
- recommendations.push('La calidad de la imagen es óptima para la captura');
226
- }
227
- return recommendations;
228
- }
229
- calculateOverallQuality(blur, reflection, focus, brightness, contrast) {
230
- let score = 0;
231
- // Blur contribution (25%)
232
- score += blur.isAcceptable ? 25 : Math.max(0, (blur.blurScore / this.BLUR_THRESHOLD) * 25);
233
- // Reflection contribution (20%)
234
- score += reflection.hasReflection ? Math.max(0, 20 - reflection.reflectionScore * 2) : 20;
235
- // Focus contribution (25%)
236
- score += focus.isFocused ? 25 : Math.max(0, (focus.focusScore / this.FOCUS_THRESHOLD) * 25);
237
- // Brightness contribution (15%)
238
- if (brightness.isAcceptable) {
239
- score += 15;
240
- }
241
- else {
242
- const brightnessPenalty = Math.abs(brightness.brightness - (brightness.minThreshold + brightness.maxThreshold) / 2);
243
- score += Math.max(0, 15 - brightnessPenalty / 10);
244
- }
245
- // Contrast contribution (15%)
246
- score += contrast.isAcceptable ? 15 : Math.max(0, (contrast.contrast / this.MIN_CONTRAST) * 15);
247
- return Math.round(Math.max(0, Math.min(100, score)));
248
- }
249
- getQualityLevel(score) {
250
- if (score >= 85)
251
- return 'excellent';
252
- if (score >= 70)
253
- return 'good';
254
- if (score >= 50)
255
- return 'acceptable';
256
- return 'poor';
257
- }
258
- identifyIssues(blur, reflection, focus, brightness, contrast) {
259
- const issues = [];
260
- if (!blur.isAcceptable) {
261
- issues.push('Imagen desenfocada por movimiento');
262
- }
263
- if (reflection.hasReflection) {
264
- issues.push('Reflejos detectados en el documento');
265
- }
266
- if (!focus.isFocused) {
267
- issues.push('Documento fuera de foco');
268
- }
269
- if (!brightness.isAcceptable) {
270
- if (brightness.brightness < brightness.minThreshold) {
271
- issues.push('Iluminación insuficiente');
272
- }
273
- else {
274
- issues.push('Imagen sobreexpuesta');
275
- }
276
- }
277
- if (!contrast.isAcceptable) {
278
- issues.push('Contraste insuficiente');
279
- }
280
- return issues;
281
- }
282
- getGrayscale(data, x, y, width) {
283
- const idx = (y * width + x) * 4;
284
- return 0.299 * data[idx] + 0.587 * data[idx + 1] + 0.114 * data[idx + 2];
285
- }
286
- findConnectedRegion(brightRegions, visited, startX, startY, width, height) {
287
- const stack = [{ x: startX, y: startY }];
288
- let size = 0;
289
- let minX = startX, maxX = startX, minY = startY, maxY = startY;
290
- while (stack.length > 0) {
291
- const { x, y } = stack.pop();
292
- if (x < 0 || x >= width || y < 0 || y >= height || visited[y][x] || !brightRegions[y][x]) {
293
- continue;
294
- }
295
- visited[y][x] = true;
296
- size++;
297
- minX = Math.min(minX, x);
298
- maxX = Math.max(maxX, x);
299
- minY = Math.min(minY, y);
300
- maxY = Math.max(maxY, y);
301
- // Add adjacent pixels
302
- stack.push({ x: x + 1, y }, { x: x - 1, y }, { x, y: y + 1 }, { x, y: y - 1 });
303
- }
304
- return { size, minX, maxX, minY, maxY };
305
- }
306
- sobelX(data, x, y, width) {
307
- const kernel = [[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]];
308
- let sum = 0;
309
- for (let ky = -1; ky <= 1; ky++) {
310
- for (let kx = -1; kx <= 1; kx++) {
311
- const gray = this.getGrayscale(data, x + kx, y + ky, width);
312
- sum += gray * kernel[ky + 1][kx + 1];
313
- }
314
- }
315
- return sum;
316
- }
317
- sobelY(data, x, y, width) {
318
- const kernel = [[-1, -2, -1], [0, 0, 0], [1, 2, 1]];
319
- let sum = 0;
320
- for (let ky = -1; ky <= 1; ky++) {
321
- for (let kx = -1; kx <= 1; kx++) {
322
- const gray = this.getGrayscale(data, x + kx, y + ky, width);
323
- sum += gray * kernel[ky + 1][kx + 1];
324
- }
325
- }
326
- return sum;
327
- }
328
- }
329
- //# sourceMappingURL=ImageQualityService.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ImageQualityService.js","sourceRoot":"","sources":["../../src/services/ImageQualityService.ts"],"names":[],"mappings":"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","sourcesContent":["import { \n IImageQualityService, \n ImageQualityResult, \n BlurMetrics, \n ReflectionMetrics, \n FocusMetrics, \n BrightnessMetrics, \n ContrastMetrics \n} from './interfaces/IImageQualityService';\nimport { ILogger } from './interfaces/ILogger';\n\nexport class ImageQualityService implements IImageQualityService {\n // Thresholds for quality assessment\n private readonly BLUR_THRESHOLD = 80; // Reduced from 100 to 80 for better acceptance\n private readonly FOCUS_THRESHOLD = 40; // Reduced from 50 to 40 for better acceptance\n private readonly REFLECTION_THRESHOLD = 240; // Pixel brightness threshold for reflections\n private readonly MIN_BRIGHTNESS = 50; // Minimum acceptable brightness\n private readonly MAX_BRIGHTNESS = 200; // Maximum acceptable brightness\n private readonly MIN_CONTRAST = 30; // Minimum acceptable contrast\n\n constructor(private logger: ILogger) {}\n\n analyzeImageQuality(canvas: HTMLCanvasElement, documentBounds?: { x: number; y: number; w: number; h: number }): ImageQualityResult {\n const ctx = canvas.getContext('2d');\n if (!ctx) {\n throw new Error('Unable to get canvas context');\n }\n\n // Get image data for the document area or full canvas\n let imageData: ImageData;\n if (documentBounds) {\n imageData = ctx.getImageData(documentBounds.x, documentBounds.y, documentBounds.w, documentBounds.h);\n } else {\n imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);\n }\n\n // Perform all quality assessments\n const blur = this.detectBlur(imageData);\n const reflection = this.detectReflections(imageData);\n const focus = this.measureFocus(imageData);\n const brightness = this.measureBrightness(imageData);\n const contrast = this.measureContrast(imageData);\n\n // Calculate overall quality score (0-100)\n const qualityScore = this.calculateOverallQuality(blur, reflection, focus, brightness, contrast);\n const overallQuality = this.getQualityLevel(qualityScore);\n \n // Identify issues and recommendations\n const issues = this.identifyIssues(blur, reflection, focus, brightness, contrast);\n const recommendations = this.getQualityRecommendations({ \n blur, reflection, focus, brightness, contrast, \n overallQuality, qualityScore, issues, recommendations: [] \n });\n\n const result: ImageQualityResult = {\n blur,\n reflection,\n focus,\n brightness,\n contrast,\n overallQuality,\n qualityScore,\n issues,\n recommendations\n };\n\n this.logger.state('CALIDAD_IMAGEN_ANALIZADA', {\n qualityScore,\n overallQuality,\n issues: issues.length,\n hasBlur: !blur.isAcceptable,\n hasReflection: reflection.hasReflection,\n isFocused: focus.isFocused\n });\n\n return result;\n }\n\n detectBlur(imageData: ImageData): BlurMetrics {\n const { data, width, height } = imageData;\n let totalVariance = 0;\n let pixelCount = 0;\n\n // Calculate Laplacian variance for blur detection\n for (let y = 1; y < height - 1; y++) {\n for (let x = 1; x < width - 1; x++) {\n const idx = (y * width + x) * 4;\n \n // Convert to grayscale\n const gray = 0.299 * data[idx] + 0.587 * data[idx + 1] + 0.114 * data[idx + 2];\n \n // Calculate Laplacian (edge detection)\n const neighbors = [\n this.getGrayscale(data, x - 1, y - 1, width),\n this.getGrayscale(data, x, y - 1, width),\n this.getGrayscale(data, x + 1, y - 1, width),\n this.getGrayscale(data, x - 1, y, width),\n this.getGrayscale(data, x + 1, y, width),\n this.getGrayscale(data, x - 1, y + 1, width),\n this.getGrayscale(data, x, y + 1, width),\n this.getGrayscale(data, x + 1, y + 1, width)\n ];\n \n const laplacian = 8 * gray - neighbors.reduce((sum, val) => sum + val, 0);\n totalVariance += laplacian * laplacian;\n pixelCount++;\n }\n }\n\n const blurScore = Math.sqrt(totalVariance / pixelCount);\n const isAcceptable = blurScore > this.BLUR_THRESHOLD;\n\n return {\n blurScore,\n isAcceptable,\n threshold: this.BLUR_THRESHOLD\n };\n }\n\n detectReflections(imageData: ImageData): ReflectionMetrics {\n const { data, width, height } = imageData;\n let reflectionPixels = 0;\n const totalPixels = width * height;\n const reflectionAreas: { x: number; y: number; width: number; height: number }[] = [];\n \n // Track bright regions that could be reflections\n const brightRegions: boolean[][] = Array(height).fill(null).map(() => Array(width).fill(false));\n \n for (let y = 0; y < height; y++) {\n for (let x = 0; x < width; x++) {\n const idx = (y * width + x) * 4;\n const brightness = (data[idx] + data[idx + 1] + data[idx + 2]) / 3;\n \n if (brightness > this.REFLECTION_THRESHOLD) {\n brightRegions[y][x] = true;\n reflectionPixels++;\n }\n }\n }\n\n // Find connected bright regions (potential reflections)\n const visited: boolean[][] = Array(height).fill(null).map(() => Array(width).fill(false));\n \n for (let y = 0; y < height; y++) {\n for (let x = 0; x < width; x++) {\n if (brightRegions[y][x] && !visited[y][x]) {\n const region = this.findConnectedRegion(brightRegions, visited, x, y, width, height);\n if (region.size > 100) { // Minimum size for a reflection\n reflectionAreas.push({\n x: region.minX,\n y: region.minY,\n width: region.maxX - region.minX,\n height: region.maxY - region.minY\n });\n }\n }\n }\n }\n\n const reflectionScore = (reflectionPixels / totalPixels) * 100;\n const hasReflection = reflectionScore > 5 || reflectionAreas.length > 0; // 5% threshold\n\n return {\n reflectionScore,\n hasReflection,\n threshold: 5,\n reflectionAreas\n };\n }\n\n measureFocus(imageData: ImageData): FocusMetrics {\n const { data, width, height } = imageData;\n let edgeStrength = 0;\n let edgeCount = 0;\n\n // Sobel edge detection for focus measurement\n for (let y = 1; y < height - 1; y++) {\n for (let x = 1; x < width - 1; x++) {\n const gx = this.sobelX(data, x, y, width);\n const gy = this.sobelY(data, x, y, width);\n const magnitude = Math.sqrt(gx * gx + gy * gy);\n \n edgeStrength += magnitude;\n edgeCount++;\n }\n }\n\n const focusScore = edgeCount > 0 ? edgeStrength / edgeCount : 0;\n const isFocused = focusScore > this.FOCUS_THRESHOLD;\n\n return {\n focusScore,\n isFocused,\n threshold: this.FOCUS_THRESHOLD,\n edgeStrength\n };\n }\n\n measureBrightness(imageData: ImageData): BrightnessMetrics {\n const { data } = imageData;\n let totalBrightness = 0;\n const pixelCount = data.length / 4;\n\n for (let i = 0; i < data.length; i += 4) {\n const brightness = (data[i] + data[i + 1] + data[i + 2]) / 3;\n totalBrightness += brightness;\n }\n\n const brightness = totalBrightness / pixelCount;\n const isAcceptable = brightness >= this.MIN_BRIGHTNESS && brightness <= this.MAX_BRIGHTNESS;\n\n return {\n brightness,\n isAcceptable,\n minThreshold: this.MIN_BRIGHTNESS,\n maxThreshold: this.MAX_BRIGHTNESS\n };\n }\n\n measureContrast(imageData: ImageData): ContrastMetrics {\n const { data } = imageData;\n let min = 255;\n let max = 0;\n\n for (let i = 0; i < data.length; i += 4) {\n const brightness = (data[i] + data[i + 1] + data[i + 2]) / 3;\n min = Math.min(min, brightness);\n max = Math.max(max, brightness);\n }\n\n const contrast = max - min;\n const isAcceptable = contrast > this.MIN_CONTRAST;\n\n return {\n contrast,\n isAcceptable,\n threshold: this.MIN_CONTRAST\n };\n }\n\n getQualityRecommendations(result: ImageQualityResult): string[] {\n const recommendations: string[] = [];\n\n if (!result.blur.isAcceptable) {\n recommendations.push('Mantenga el dispositivo estable para reducir el desenfoque');\n recommendations.push('Acérquese al documento para mejorar la nitidez');\n }\n\n if (result.reflection.hasReflection) {\n recommendations.push('Cambie el ángulo del dispositivo para evitar reflejos');\n recommendations.push('Mejore la iluminación indirecta del área');\n }\n\n if (!result.focus.isFocused) {\n recommendations.push('Permita que la cámara enfoque automáticamente');\n recommendations.push('Toque la pantalla sobre el documento para enfocar');\n }\n\n if (!result.brightness.isAcceptable) {\n if (result.brightness.brightness < result.brightness.minThreshold) {\n recommendations.push('Aumente la iluminación del área');\n recommendations.push('Muévase a un lugar con mejor luz');\n } else {\n recommendations.push('Reduzca la iluminación directa sobre el documento');\n recommendations.push('Evite la luz solar directa');\n }\n }\n\n if (!result.contrast.isAcceptable) {\n recommendations.push('Mejore la iluminación para aumentar el contraste');\n recommendations.push('Asegúrese de que el fondo contraste con el documento');\n }\n\n if (recommendations.length === 0) {\n recommendations.push('La calidad de la imagen es óptima para la captura');\n }\n\n return recommendations;\n }\n\n private calculateOverallQuality(\n blur: BlurMetrics, \n reflection: ReflectionMetrics, \n focus: FocusMetrics, \n brightness: BrightnessMetrics, \n contrast: ContrastMetrics\n ): number {\n let score = 0;\n\n // Blur contribution (25%)\n score += blur.isAcceptable ? 25 : Math.max(0, (blur.blurScore / this.BLUR_THRESHOLD) * 25);\n\n // Reflection contribution (20%)\n score += reflection.hasReflection ? Math.max(0, 20 - reflection.reflectionScore * 2) : 20;\n\n // Focus contribution (25%)\n score += focus.isFocused ? 25 : Math.max(0, (focus.focusScore / this.FOCUS_THRESHOLD) * 25);\n\n // Brightness contribution (15%)\n if (brightness.isAcceptable) {\n score += 15;\n } else {\n const brightnessPenalty = Math.abs(brightness.brightness - (brightness.minThreshold + brightness.maxThreshold) / 2);\n score += Math.max(0, 15 - brightnessPenalty / 10);\n }\n\n // Contrast contribution (15%)\n score += contrast.isAcceptable ? 15 : Math.max(0, (contrast.contrast / this.MIN_CONTRAST) * 15);\n\n return Math.round(Math.max(0, Math.min(100, score)));\n }\n\n private getQualityLevel(score: number): 'excellent' | 'good' | 'acceptable' | 'poor' {\n if (score >= 85) return 'excellent';\n if (score >= 70) return 'good';\n if (score >= 50) return 'acceptable';\n return 'poor';\n }\n\n private identifyIssues(\n blur: BlurMetrics, \n reflection: ReflectionMetrics, \n focus: FocusMetrics, \n brightness: BrightnessMetrics, \n contrast: ContrastMetrics\n ): string[] {\n const issues: string[] = [];\n\n if (!blur.isAcceptable) {\n issues.push('Imagen desenfocada por movimiento');\n }\n\n if (reflection.hasReflection) {\n issues.push('Reflejos detectados en el documento');\n }\n\n if (!focus.isFocused) {\n issues.push('Documento fuera de foco');\n }\n\n if (!brightness.isAcceptable) {\n if (brightness.brightness < brightness.minThreshold) {\n issues.push('Iluminación insuficiente');\n } else {\n issues.push('Imagen sobreexpuesta');\n }\n }\n\n if (!contrast.isAcceptable) {\n issues.push('Contraste insuficiente');\n }\n\n return issues;\n }\n\n private getGrayscale(data: Uint8ClampedArray, x: number, y: number, width: number): number {\n const idx = (y * width + x) * 4;\n return 0.299 * data[idx] + 0.587 * data[idx + 1] + 0.114 * data[idx + 2];\n }\n\n private findConnectedRegion(\n brightRegions: boolean[][], \n visited: boolean[][], \n startX: number, \n startY: number, \n width: number, \n height: number\n ) {\n const stack = [{ x: startX, y: startY }];\n let size = 0;\n let minX = startX, maxX = startX, minY = startY, maxY = startY;\n\n while (stack.length > 0) {\n const { x, y } = stack.pop()!;\n \n if (x < 0 || x >= width || y < 0 || y >= height || visited[y][x] || !brightRegions[y][x]) {\n continue;\n }\n\n visited[y][x] = true;\n size++;\n \n minX = Math.min(minX, x);\n maxX = Math.max(maxX, x);\n minY = Math.min(minY, y);\n maxY = Math.max(maxY, y);\n\n // Add adjacent pixels\n stack.push({ x: x + 1, y }, { x: x - 1, y }, { x, y: y + 1 }, { x, y: y - 1 });\n }\n\n return { size, minX, maxX, minY, maxY };\n }\n\n private sobelX(data: Uint8ClampedArray, x: number, y: number, width: number): number {\n const kernel = [[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]];\n let sum = 0;\n\n for (let ky = -1; ky <= 1; ky++) {\n for (let kx = -1; kx <= 1; kx++) {\n const gray = this.getGrayscale(data, x + kx, y + ky, width);\n sum += gray * kernel[ky + 1][kx + 1];\n }\n }\n\n return sum;\n }\n\n private sobelY(data: Uint8ClampedArray, x: number, y: number, width: number): number {\n const kernel = [[-1, -2, -1], [0, 0, 0], [1, 2, 1]];\n let sum = 0;\n\n for (let ky = -1; ky <= 1; ky++) {\n for (let kx = -1; kx <= 1; kx++) {\n const gray = this.getGrayscale(data, x + kx, y + ky, width);\n sum += gray * kernel[ky + 1][kx + 1];\n }\n }\n\n return sum;\n }\n}"]}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=IImageQualityService.js.map