@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.
- package/dist/cjs/jaak-stamps-webcomponent.cjs.js +1 -1
- package/dist/cjs/jaak-stamps.cjs.entry.js +14 -652
- package/dist/cjs/jaak-stamps.cjs.entry.js.map +1 -1
- package/dist/cjs/jaak-stamps.entry.cjs.js.map +1 -1
- package/dist/cjs/loader.cjs.js +1 -1
- package/dist/collection/components/my-component/my-component.css +40 -0
- package/dist/collection/components/my-component/my-component.js +35 -343
- package/dist/collection/components/my-component/my-component.js.map +1 -1
- package/dist/collection/services/DetectionService.js +1 -164
- package/dist/collection/services/DetectionService.js.map +1 -1
- package/dist/collection/services/ServiceContainer.js +1 -6
- package/dist/collection/services/ServiceContainer.js.map +1 -1
- package/dist/collection/services/interfaces/IDetectionService.js.map +1 -1
- package/dist/collection/types/component-types.js +2 -0
- package/dist/collection/types/component-types.js.map +1 -0
- package/dist/components/jaak-stamps.js +15 -663
- package/dist/components/jaak-stamps.js.map +1 -1
- package/dist/esm/jaak-stamps-webcomponent.js +1 -1
- package/dist/esm/jaak-stamps.entry.js +14 -652
- package/dist/esm/jaak-stamps.entry.js.map +1 -1
- package/dist/esm/loader.js +1 -1
- package/dist/jaak-stamps-webcomponent/jaak-stamps-webcomponent.esm.js +1 -1
- package/dist/jaak-stamps-webcomponent/jaak-stamps.entry.esm.js.map +1 -1
- package/dist/jaak-stamps-webcomponent/p-3074405d.entry.js +2 -0
- package/dist/jaak-stamps-webcomponent/p-3074405d.entry.js.map +1 -0
- package/dist/types/components/my-component/my-component.d.ts +4 -45
- package/dist/types/components.d.ts +11 -62
- package/dist/types/services/DetectionService.d.ts +2 -22
- package/dist/types/services/ServiceContainer.d.ts +0 -6
- package/dist/types/services/interfaces/IDetectionService.d.ts +0 -13
- package/dist/types/types/component-types.d.ts +36 -0
- package/package.json +1 -1
- package/dist/collection/services/ImageQualityService.js +0 -329
- package/dist/collection/services/ImageQualityService.js.map +0 -1
- package/dist/collection/services/interfaces/IImageQualityService.js +0 -2
- package/dist/collection/services/interfaces/IImageQualityService.js.map +0 -1
- package/dist/jaak-stamps-webcomponent/p-47f37982.entry.js +0 -2
- package/dist/jaak-stamps-webcomponent/p-47f37982.entry.js.map +0 -1
- package/dist/types/services/ImageQualityService.d.ts +0 -31
- package/dist/types/services/interfaces/IImageQualityService.d.ts +0 -58
|
@@ -720,346 +720,15 @@ class DeviceStrategyFactory {
|
|
|
720
720
|
}
|
|
721
721
|
}
|
|
722
722
|
|
|
723
|
-
class ImageQualityService {
|
|
724
|
-
logger;
|
|
725
|
-
// Thresholds for quality assessment
|
|
726
|
-
BLUR_THRESHOLD = 80; // Reduced from 100 to 80 for better acceptance
|
|
727
|
-
FOCUS_THRESHOLD = 40; // Reduced from 50 to 40 for better acceptance
|
|
728
|
-
REFLECTION_THRESHOLD = 240; // Pixel brightness threshold for reflections
|
|
729
|
-
MIN_BRIGHTNESS = 50; // Minimum acceptable brightness
|
|
730
|
-
MAX_BRIGHTNESS = 200; // Maximum acceptable brightness
|
|
731
|
-
MIN_CONTRAST = 30; // Minimum acceptable contrast
|
|
732
|
-
constructor(logger) {
|
|
733
|
-
this.logger = logger;
|
|
734
|
-
}
|
|
735
|
-
analyzeImageQuality(canvas, documentBounds) {
|
|
736
|
-
const ctx = canvas.getContext('2d');
|
|
737
|
-
if (!ctx) {
|
|
738
|
-
throw new Error('Unable to get canvas context');
|
|
739
|
-
}
|
|
740
|
-
// Get image data for the document area or full canvas
|
|
741
|
-
let imageData;
|
|
742
|
-
if (documentBounds) {
|
|
743
|
-
imageData = ctx.getImageData(documentBounds.x, documentBounds.y, documentBounds.w, documentBounds.h);
|
|
744
|
-
}
|
|
745
|
-
else {
|
|
746
|
-
imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
|
747
|
-
}
|
|
748
|
-
// Perform all quality assessments
|
|
749
|
-
const blur = this.detectBlur(imageData);
|
|
750
|
-
const reflection = this.detectReflections(imageData);
|
|
751
|
-
const focus = this.measureFocus(imageData);
|
|
752
|
-
const brightness = this.measureBrightness(imageData);
|
|
753
|
-
const contrast = this.measureContrast(imageData);
|
|
754
|
-
// Calculate overall quality score (0-100)
|
|
755
|
-
const qualityScore = this.calculateOverallQuality(blur, reflection, focus, brightness, contrast);
|
|
756
|
-
const overallQuality = this.getQualityLevel(qualityScore);
|
|
757
|
-
// Identify issues and recommendations
|
|
758
|
-
const issues = this.identifyIssues(blur, reflection, focus, brightness, contrast);
|
|
759
|
-
const recommendations = this.getQualityRecommendations({
|
|
760
|
-
blur, reflection, focus, brightness, contrast,
|
|
761
|
-
overallQuality, qualityScore, issues, recommendations: []
|
|
762
|
-
});
|
|
763
|
-
const result = {
|
|
764
|
-
blur,
|
|
765
|
-
reflection,
|
|
766
|
-
focus,
|
|
767
|
-
brightness,
|
|
768
|
-
contrast,
|
|
769
|
-
overallQuality,
|
|
770
|
-
qualityScore,
|
|
771
|
-
issues,
|
|
772
|
-
recommendations
|
|
773
|
-
};
|
|
774
|
-
this.logger.state('CALIDAD_IMAGEN_ANALIZADA', {
|
|
775
|
-
qualityScore,
|
|
776
|
-
overallQuality,
|
|
777
|
-
issues: issues.length,
|
|
778
|
-
hasBlur: !blur.isAcceptable,
|
|
779
|
-
hasReflection: reflection.hasReflection,
|
|
780
|
-
isFocused: focus.isFocused
|
|
781
|
-
});
|
|
782
|
-
return result;
|
|
783
|
-
}
|
|
784
|
-
detectBlur(imageData) {
|
|
785
|
-
const { data, width, height } = imageData;
|
|
786
|
-
let totalVariance = 0;
|
|
787
|
-
let pixelCount = 0;
|
|
788
|
-
// Calculate Laplacian variance for blur detection
|
|
789
|
-
for (let y = 1; y < height - 1; y++) {
|
|
790
|
-
for (let x = 1; x < width - 1; x++) {
|
|
791
|
-
const idx = (y * width + x) * 4;
|
|
792
|
-
// Convert to grayscale
|
|
793
|
-
const gray = 0.299 * data[idx] + 0.587 * data[idx + 1] + 0.114 * data[idx + 2];
|
|
794
|
-
// Calculate Laplacian (edge detection)
|
|
795
|
-
const neighbors = [
|
|
796
|
-
this.getGrayscale(data, x - 1, y - 1, width),
|
|
797
|
-
this.getGrayscale(data, x, y - 1, width),
|
|
798
|
-
this.getGrayscale(data, x + 1, y - 1, width),
|
|
799
|
-
this.getGrayscale(data, x - 1, y, width),
|
|
800
|
-
this.getGrayscale(data, x + 1, y, width),
|
|
801
|
-
this.getGrayscale(data, x - 1, y + 1, width),
|
|
802
|
-
this.getGrayscale(data, x, y + 1, width),
|
|
803
|
-
this.getGrayscale(data, x + 1, y + 1, width)
|
|
804
|
-
];
|
|
805
|
-
const laplacian = 8 * gray - neighbors.reduce((sum, val) => sum + val, 0);
|
|
806
|
-
totalVariance += laplacian * laplacian;
|
|
807
|
-
pixelCount++;
|
|
808
|
-
}
|
|
809
|
-
}
|
|
810
|
-
const blurScore = Math.sqrt(totalVariance / pixelCount);
|
|
811
|
-
const isAcceptable = blurScore > this.BLUR_THRESHOLD;
|
|
812
|
-
return {
|
|
813
|
-
blurScore,
|
|
814
|
-
isAcceptable,
|
|
815
|
-
threshold: this.BLUR_THRESHOLD
|
|
816
|
-
};
|
|
817
|
-
}
|
|
818
|
-
detectReflections(imageData) {
|
|
819
|
-
const { data, width, height } = imageData;
|
|
820
|
-
let reflectionPixels = 0;
|
|
821
|
-
const totalPixels = width * height;
|
|
822
|
-
const reflectionAreas = [];
|
|
823
|
-
// Track bright regions that could be reflections
|
|
824
|
-
const brightRegions = Array(height).fill(null).map(() => Array(width).fill(false));
|
|
825
|
-
for (let y = 0; y < height; y++) {
|
|
826
|
-
for (let x = 0; x < width; x++) {
|
|
827
|
-
const idx = (y * width + x) * 4;
|
|
828
|
-
const brightness = (data[idx] + data[idx + 1] + data[idx + 2]) / 3;
|
|
829
|
-
if (brightness > this.REFLECTION_THRESHOLD) {
|
|
830
|
-
brightRegions[y][x] = true;
|
|
831
|
-
reflectionPixels++;
|
|
832
|
-
}
|
|
833
|
-
}
|
|
834
|
-
}
|
|
835
|
-
// Find connected bright regions (potential reflections)
|
|
836
|
-
const visited = Array(height).fill(null).map(() => Array(width).fill(false));
|
|
837
|
-
for (let y = 0; y < height; y++) {
|
|
838
|
-
for (let x = 0; x < width; x++) {
|
|
839
|
-
if (brightRegions[y][x] && !visited[y][x]) {
|
|
840
|
-
const region = this.findConnectedRegion(brightRegions, visited, x, y, width, height);
|
|
841
|
-
if (region.size > 100) { // Minimum size for a reflection
|
|
842
|
-
reflectionAreas.push({
|
|
843
|
-
x: region.minX,
|
|
844
|
-
y: region.minY,
|
|
845
|
-
width: region.maxX - region.minX,
|
|
846
|
-
height: region.maxY - region.minY
|
|
847
|
-
});
|
|
848
|
-
}
|
|
849
|
-
}
|
|
850
|
-
}
|
|
851
|
-
}
|
|
852
|
-
const reflectionScore = (reflectionPixels / totalPixels) * 100;
|
|
853
|
-
const hasReflection = reflectionScore > 5 || reflectionAreas.length > 0; // 5% threshold
|
|
854
|
-
return {
|
|
855
|
-
reflectionScore,
|
|
856
|
-
hasReflection,
|
|
857
|
-
threshold: 5,
|
|
858
|
-
reflectionAreas
|
|
859
|
-
};
|
|
860
|
-
}
|
|
861
|
-
measureFocus(imageData) {
|
|
862
|
-
const { data, width, height } = imageData;
|
|
863
|
-
let edgeStrength = 0;
|
|
864
|
-
let edgeCount = 0;
|
|
865
|
-
// Sobel edge detection for focus measurement
|
|
866
|
-
for (let y = 1; y < height - 1; y++) {
|
|
867
|
-
for (let x = 1; x < width - 1; x++) {
|
|
868
|
-
const gx = this.sobelX(data, x, y, width);
|
|
869
|
-
const gy = this.sobelY(data, x, y, width);
|
|
870
|
-
const magnitude = Math.sqrt(gx * gx + gy * gy);
|
|
871
|
-
edgeStrength += magnitude;
|
|
872
|
-
edgeCount++;
|
|
873
|
-
}
|
|
874
|
-
}
|
|
875
|
-
const focusScore = edgeCount > 0 ? edgeStrength / edgeCount : 0;
|
|
876
|
-
const isFocused = focusScore > this.FOCUS_THRESHOLD;
|
|
877
|
-
return {
|
|
878
|
-
focusScore,
|
|
879
|
-
isFocused,
|
|
880
|
-
threshold: this.FOCUS_THRESHOLD,
|
|
881
|
-
edgeStrength
|
|
882
|
-
};
|
|
883
|
-
}
|
|
884
|
-
measureBrightness(imageData) {
|
|
885
|
-
const { data } = imageData;
|
|
886
|
-
let totalBrightness = 0;
|
|
887
|
-
const pixelCount = data.length / 4;
|
|
888
|
-
for (let i = 0; i < data.length; i += 4) {
|
|
889
|
-
const brightness = (data[i] + data[i + 1] + data[i + 2]) / 3;
|
|
890
|
-
totalBrightness += brightness;
|
|
891
|
-
}
|
|
892
|
-
const brightness = totalBrightness / pixelCount;
|
|
893
|
-
const isAcceptable = brightness >= this.MIN_BRIGHTNESS && brightness <= this.MAX_BRIGHTNESS;
|
|
894
|
-
return {
|
|
895
|
-
brightness,
|
|
896
|
-
isAcceptable,
|
|
897
|
-
minThreshold: this.MIN_BRIGHTNESS,
|
|
898
|
-
maxThreshold: this.MAX_BRIGHTNESS
|
|
899
|
-
};
|
|
900
|
-
}
|
|
901
|
-
measureContrast(imageData) {
|
|
902
|
-
const { data } = imageData;
|
|
903
|
-
let min = 255;
|
|
904
|
-
let max = 0;
|
|
905
|
-
for (let i = 0; i < data.length; i += 4) {
|
|
906
|
-
const brightness = (data[i] + data[i + 1] + data[i + 2]) / 3;
|
|
907
|
-
min = Math.min(min, brightness);
|
|
908
|
-
max = Math.max(max, brightness);
|
|
909
|
-
}
|
|
910
|
-
const contrast = max - min;
|
|
911
|
-
const isAcceptable = contrast > this.MIN_CONTRAST;
|
|
912
|
-
return {
|
|
913
|
-
contrast,
|
|
914
|
-
isAcceptable,
|
|
915
|
-
threshold: this.MIN_CONTRAST
|
|
916
|
-
};
|
|
917
|
-
}
|
|
918
|
-
getQualityRecommendations(result) {
|
|
919
|
-
const recommendations = [];
|
|
920
|
-
if (!result.blur.isAcceptable) {
|
|
921
|
-
recommendations.push('Mantenga el dispositivo estable para reducir el desenfoque');
|
|
922
|
-
recommendations.push('Acérquese al documento para mejorar la nitidez');
|
|
923
|
-
}
|
|
924
|
-
if (result.reflection.hasReflection) {
|
|
925
|
-
recommendations.push('Cambie el ángulo del dispositivo para evitar reflejos');
|
|
926
|
-
recommendations.push('Mejore la iluminación indirecta del área');
|
|
927
|
-
}
|
|
928
|
-
if (!result.focus.isFocused) {
|
|
929
|
-
recommendations.push('Permita que la cámara enfoque automáticamente');
|
|
930
|
-
recommendations.push('Toque la pantalla sobre el documento para enfocar');
|
|
931
|
-
}
|
|
932
|
-
if (!result.brightness.isAcceptable) {
|
|
933
|
-
if (result.brightness.brightness < result.brightness.minThreshold) {
|
|
934
|
-
recommendations.push('Aumente la iluminación del área');
|
|
935
|
-
recommendations.push('Muévase a un lugar con mejor luz');
|
|
936
|
-
}
|
|
937
|
-
else {
|
|
938
|
-
recommendations.push('Reduzca la iluminación directa sobre el documento');
|
|
939
|
-
recommendations.push('Evite la luz solar directa');
|
|
940
|
-
}
|
|
941
|
-
}
|
|
942
|
-
if (!result.contrast.isAcceptable) {
|
|
943
|
-
recommendations.push('Mejore la iluminación para aumentar el contraste');
|
|
944
|
-
recommendations.push('Asegúrese de que el fondo contraste con el documento');
|
|
945
|
-
}
|
|
946
|
-
if (recommendations.length === 0) {
|
|
947
|
-
recommendations.push('La calidad de la imagen es óptima para la captura');
|
|
948
|
-
}
|
|
949
|
-
return recommendations;
|
|
950
|
-
}
|
|
951
|
-
calculateOverallQuality(blur, reflection, focus, brightness, contrast) {
|
|
952
|
-
let score = 0;
|
|
953
|
-
// Blur contribution (25%)
|
|
954
|
-
score += blur.isAcceptable ? 25 : Math.max(0, (blur.blurScore / this.BLUR_THRESHOLD) * 25);
|
|
955
|
-
// Reflection contribution (20%)
|
|
956
|
-
score += reflection.hasReflection ? Math.max(0, 20 - reflection.reflectionScore * 2) : 20;
|
|
957
|
-
// Focus contribution (25%)
|
|
958
|
-
score += focus.isFocused ? 25 : Math.max(0, (focus.focusScore / this.FOCUS_THRESHOLD) * 25);
|
|
959
|
-
// Brightness contribution (15%)
|
|
960
|
-
if (brightness.isAcceptable) {
|
|
961
|
-
score += 15;
|
|
962
|
-
}
|
|
963
|
-
else {
|
|
964
|
-
const brightnessPenalty = Math.abs(brightness.brightness - (brightness.minThreshold + brightness.maxThreshold) / 2);
|
|
965
|
-
score += Math.max(0, 15 - brightnessPenalty / 10);
|
|
966
|
-
}
|
|
967
|
-
// Contrast contribution (15%)
|
|
968
|
-
score += contrast.isAcceptable ? 15 : Math.max(0, (contrast.contrast / this.MIN_CONTRAST) * 15);
|
|
969
|
-
return Math.round(Math.max(0, Math.min(100, score)));
|
|
970
|
-
}
|
|
971
|
-
getQualityLevel(score) {
|
|
972
|
-
if (score >= 85)
|
|
973
|
-
return 'excellent';
|
|
974
|
-
if (score >= 70)
|
|
975
|
-
return 'good';
|
|
976
|
-
if (score >= 50)
|
|
977
|
-
return 'acceptable';
|
|
978
|
-
return 'poor';
|
|
979
|
-
}
|
|
980
|
-
identifyIssues(blur, reflection, focus, brightness, contrast) {
|
|
981
|
-
const issues = [];
|
|
982
|
-
if (!blur.isAcceptable) {
|
|
983
|
-
issues.push('Imagen desenfocada por movimiento');
|
|
984
|
-
}
|
|
985
|
-
if (reflection.hasReflection) {
|
|
986
|
-
issues.push('Reflejos detectados en el documento');
|
|
987
|
-
}
|
|
988
|
-
if (!focus.isFocused) {
|
|
989
|
-
issues.push('Documento fuera de foco');
|
|
990
|
-
}
|
|
991
|
-
if (!brightness.isAcceptable) {
|
|
992
|
-
if (brightness.brightness < brightness.minThreshold) {
|
|
993
|
-
issues.push('Iluminación insuficiente');
|
|
994
|
-
}
|
|
995
|
-
else {
|
|
996
|
-
issues.push('Imagen sobreexpuesta');
|
|
997
|
-
}
|
|
998
|
-
}
|
|
999
|
-
if (!contrast.isAcceptable) {
|
|
1000
|
-
issues.push('Contraste insuficiente');
|
|
1001
|
-
}
|
|
1002
|
-
return issues;
|
|
1003
|
-
}
|
|
1004
|
-
getGrayscale(data, x, y, width) {
|
|
1005
|
-
const idx = (y * width + x) * 4;
|
|
1006
|
-
return 0.299 * data[idx] + 0.587 * data[idx + 1] + 0.114 * data[idx + 2];
|
|
1007
|
-
}
|
|
1008
|
-
findConnectedRegion(brightRegions, visited, startX, startY, width, height) {
|
|
1009
|
-
const stack = [{ x: startX, y: startY }];
|
|
1010
|
-
let size = 0;
|
|
1011
|
-
let minX = startX, maxX = startX, minY = startY, maxY = startY;
|
|
1012
|
-
while (stack.length > 0) {
|
|
1013
|
-
const { x, y } = stack.pop();
|
|
1014
|
-
if (x < 0 || x >= width || y < 0 || y >= height || visited[y][x] || !brightRegions[y][x]) {
|
|
1015
|
-
continue;
|
|
1016
|
-
}
|
|
1017
|
-
visited[y][x] = true;
|
|
1018
|
-
size++;
|
|
1019
|
-
minX = Math.min(minX, x);
|
|
1020
|
-
maxX = Math.max(maxX, x);
|
|
1021
|
-
minY = Math.min(minY, y);
|
|
1022
|
-
maxY = Math.max(maxY, y);
|
|
1023
|
-
// Add adjacent pixels
|
|
1024
|
-
stack.push({ x: x + 1, y }, { x: x - 1, y }, { x, y: y + 1 }, { x, y: y - 1 });
|
|
1025
|
-
}
|
|
1026
|
-
return { size, minX, maxX, minY, maxY };
|
|
1027
|
-
}
|
|
1028
|
-
sobelX(data, x, y, width) {
|
|
1029
|
-
const kernel = [[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]];
|
|
1030
|
-
let sum = 0;
|
|
1031
|
-
for (let ky = -1; ky <= 1; ky++) {
|
|
1032
|
-
for (let kx = -1; kx <= 1; kx++) {
|
|
1033
|
-
const gray = this.getGrayscale(data, x + kx, y + ky, width);
|
|
1034
|
-
sum += gray * kernel[ky + 1][kx + 1];
|
|
1035
|
-
}
|
|
1036
|
-
}
|
|
1037
|
-
return sum;
|
|
1038
|
-
}
|
|
1039
|
-
sobelY(data, x, y, width) {
|
|
1040
|
-
const kernel = [[-1, -2, -1], [0, 0, 0], [1, 2, 1]];
|
|
1041
|
-
let sum = 0;
|
|
1042
|
-
for (let ky = -1; ky <= 1; ky++) {
|
|
1043
|
-
for (let kx = -1; kx <= 1; kx++) {
|
|
1044
|
-
const gray = this.getGrayscale(data, x + kx, y + ky, width);
|
|
1045
|
-
sum += gray * kernel[ky + 1][kx + 1];
|
|
1046
|
-
}
|
|
1047
|
-
}
|
|
1048
|
-
return sum;
|
|
1049
|
-
}
|
|
1050
|
-
}
|
|
1051
|
-
|
|
1052
723
|
class DetectionService {
|
|
1053
724
|
logger;
|
|
1054
725
|
debug;
|
|
1055
726
|
useDocumentClassification;
|
|
1056
|
-
qualityThresholds;
|
|
1057
727
|
session;
|
|
1058
728
|
mobileNetSession;
|
|
1059
729
|
mobileNetClassMap;
|
|
1060
730
|
modelLoaded = false;
|
|
1061
731
|
deviceStrategy;
|
|
1062
|
-
imageQualityService;
|
|
1063
732
|
MODEL_PATH = "https://storage.googleapis.com/jaak-static/web/component/stamps/ddmyp-v2.onnx";
|
|
1064
733
|
MOBILENET_MODEL_PATH = "https://storage.googleapis.com/jaak-static/web/component/stamps/cdmmp-v1.onnx";
|
|
1065
734
|
MOBILENET_CLASSES_PATH = "https://storage.googleapis.com/jaak-static/web/component/stamps/cdmmp-v1.json";
|
|
@@ -1069,13 +738,11 @@ class DetectionService {
|
|
|
1069
738
|
preprocessCanvas;
|
|
1070
739
|
preprocessCtx;
|
|
1071
740
|
captureCanvas;
|
|
1072
|
-
constructor(logger, debug = false, useDocumentClassification = false
|
|
741
|
+
constructor(logger, debug = false, useDocumentClassification = false) {
|
|
1073
742
|
this.logger = logger;
|
|
1074
743
|
this.debug = debug;
|
|
1075
744
|
this.useDocumentClassification = useDocumentClassification;
|
|
1076
|
-
this.qualityThresholds = qualityThresholds;
|
|
1077
745
|
this.deviceStrategy = DeviceStrategyFactory.createStrategy();
|
|
1078
|
-
this.imageQualityService = new ImageQualityService(logger);
|
|
1079
746
|
this.initializeCanvasPool();
|
|
1080
747
|
}
|
|
1081
748
|
async loadModel() {
|
|
@@ -1419,164 +1086,6 @@ class DetectionService {
|
|
|
1419
1086
|
}
|
|
1420
1087
|
return new window.ort.Tensor('float32', arr, [1, 3, 224, 224]);
|
|
1421
1088
|
}
|
|
1422
|
-
// New methods for image quality validation
|
|
1423
|
-
validateImageQuality(video, detectedBox) {
|
|
1424
|
-
// Create a canvas to capture the current frame
|
|
1425
|
-
const canvas = document.createElement('canvas');
|
|
1426
|
-
canvas.width = video.videoWidth;
|
|
1427
|
-
canvas.height = video.videoHeight;
|
|
1428
|
-
const ctx = canvas.getContext('2d');
|
|
1429
|
-
ctx.drawImage(video, 0, 0);
|
|
1430
|
-
// If we have a detected document, focus quality analysis on that area
|
|
1431
|
-
let documentBounds;
|
|
1432
|
-
if (detectedBox) {
|
|
1433
|
-
const scaleX = video.videoWidth / this.INPUT_SIZE;
|
|
1434
|
-
const scaleY = video.videoHeight / this.INPUT_SIZE;
|
|
1435
|
-
documentBounds = {
|
|
1436
|
-
x: Math.max(0, detectedBox.x * scaleX),
|
|
1437
|
-
y: Math.max(0, detectedBox.y * scaleY),
|
|
1438
|
-
w: Math.min(detectedBox.w * scaleX, video.videoWidth),
|
|
1439
|
-
h: Math.min(detectedBox.h * scaleY, video.videoHeight)
|
|
1440
|
-
};
|
|
1441
|
-
}
|
|
1442
|
-
return this.imageQualityService.analyzeImageQuality(canvas, documentBounds);
|
|
1443
|
-
}
|
|
1444
|
-
isImageQualityAcceptable(qualityResult) {
|
|
1445
|
-
// Use configurable thresholds with fallback defaults
|
|
1446
|
-
const minQualityScore = this.qualityThresholds.minQualityScore ?? 45;
|
|
1447
|
-
const minFocusScore = this.qualityThresholds.minFocusScore ?? 16;
|
|
1448
|
-
const minBlurScore = this.qualityThresholds.minBlurScore ?? 22;
|
|
1449
|
-
const maxReflectionScore = this.qualityThresholds.maxReflectionScore ?? 15;
|
|
1450
|
-
// Check overall quality score - primary metric
|
|
1451
|
-
if (qualityResult.qualityScore < minQualityScore) {
|
|
1452
|
-
this.logger.state('QUALITY_REJECTION_SCORE', {
|
|
1453
|
-
qualityScore: qualityResult.qualityScore,
|
|
1454
|
-
minQualityScore,
|
|
1455
|
-
reason: 'score_too_low'
|
|
1456
|
-
});
|
|
1457
|
-
return false;
|
|
1458
|
-
}
|
|
1459
|
-
// Check focus score
|
|
1460
|
-
if (qualityResult.focus.focusScore < minFocusScore) {
|
|
1461
|
-
this.logger.state('QUALITY_REJECTION_FOCUS', {
|
|
1462
|
-
focusScore: qualityResult.focus.focusScore,
|
|
1463
|
-
minFocusScore,
|
|
1464
|
-
reason: 'severe_focus_issues'
|
|
1465
|
-
});
|
|
1466
|
-
return false;
|
|
1467
|
-
}
|
|
1468
|
-
// Check blur score
|
|
1469
|
-
if (qualityResult.blur.blurScore < minBlurScore) {
|
|
1470
|
-
this.logger.state('QUALITY_REJECTION_BLUR', {
|
|
1471
|
-
blurScore: qualityResult.blur.blurScore,
|
|
1472
|
-
minBlurScore,
|
|
1473
|
-
reason: 'severe_blur_issues'
|
|
1474
|
-
});
|
|
1475
|
-
return false;
|
|
1476
|
-
}
|
|
1477
|
-
// Check reflection score
|
|
1478
|
-
if (qualityResult.reflection.hasReflection && qualityResult.reflection.reflectionScore > maxReflectionScore) {
|
|
1479
|
-
this.logger.state('QUALITY_REJECTION_REFLECTION', {
|
|
1480
|
-
hasReflection: qualityResult.reflection.hasReflection,
|
|
1481
|
-
reflectionScore: qualityResult.reflection.reflectionScore,
|
|
1482
|
-
maxReflectionScore,
|
|
1483
|
-
reason: 'severe_reflection_issues'
|
|
1484
|
-
});
|
|
1485
|
-
return false;
|
|
1486
|
-
}
|
|
1487
|
-
this.logger.state('QUALITY_ACCEPTANCE', {
|
|
1488
|
-
qualityScore: qualityResult.qualityScore,
|
|
1489
|
-
focusScore: qualityResult.focus.focusScore,
|
|
1490
|
-
blurScore: qualityResult.blur.blurScore,
|
|
1491
|
-
reflectionScore: qualityResult.reflection.reflectionScore,
|
|
1492
|
-
issues: qualityResult.issues,
|
|
1493
|
-
reason: 'quality_acceptable'
|
|
1494
|
-
});
|
|
1495
|
-
return true;
|
|
1496
|
-
}
|
|
1497
|
-
updateQualityThresholds(thresholds) {
|
|
1498
|
-
this.qualityThresholds = { ...this.qualityThresholds, ...thresholds };
|
|
1499
|
-
this.logger.state('QUALITY_THRESHOLDS_UPDATED', {
|
|
1500
|
-
newThresholds: this.qualityThresholds
|
|
1501
|
-
});
|
|
1502
|
-
}
|
|
1503
|
-
getQualityThresholds() {
|
|
1504
|
-
return { ...this.qualityThresholds };
|
|
1505
|
-
}
|
|
1506
|
-
getQualityFeedback(qualityResult) {
|
|
1507
|
-
if (qualityResult.qualityScore >= 85) {
|
|
1508
|
-
return 'Calidad excelente - listo para capturar';
|
|
1509
|
-
}
|
|
1510
|
-
else if (qualityResult.qualityScore >= 70) {
|
|
1511
|
-
return 'Buena calidad - puede capturar';
|
|
1512
|
-
}
|
|
1513
|
-
else if (qualityResult.qualityScore >= 50) {
|
|
1514
|
-
const primaryIssue = qualityResult.issues[0];
|
|
1515
|
-
return primaryIssue ? `Calidad aceptable - ${primaryIssue.toLowerCase()}` : 'Calidad aceptable';
|
|
1516
|
-
}
|
|
1517
|
-
else {
|
|
1518
|
-
const mainIssues = qualityResult.issues.slice(0, 2).join(', ').toLowerCase();
|
|
1519
|
-
return `Calidad insuficiente - ${mainIssues}`;
|
|
1520
|
-
}
|
|
1521
|
-
}
|
|
1522
|
-
// Enhanced detection method that includes quality validation
|
|
1523
|
-
async runInferenceWithQuality(inputTensor, video) {
|
|
1524
|
-
const detections = await this.runInference(inputTensor);
|
|
1525
|
-
// Get the best detection for quality analysis
|
|
1526
|
-
const bestDetection = detections.length > 0 ?
|
|
1527
|
-
detections.reduce((best, current) => current.score > best.score ? current : best) :
|
|
1528
|
-
undefined;
|
|
1529
|
-
const qualityResult = this.validateImageQuality(video, bestDetection);
|
|
1530
|
-
const canCapture = this.isImageQualityAcceptable(qualityResult);
|
|
1531
|
-
const feedback = this.getQualityFeedback(qualityResult);
|
|
1532
|
-
this.logger.state('DETECCION_CON_CALIDAD', {
|
|
1533
|
-
detectionsCount: detections.length,
|
|
1534
|
-
qualityScore: qualityResult.qualityScore,
|
|
1535
|
-
canCapture,
|
|
1536
|
-
issues: qualityResult.issues.length,
|
|
1537
|
-
bestDetectionScore: bestDetection?.score || 0
|
|
1538
|
-
});
|
|
1539
|
-
return {
|
|
1540
|
-
detections,
|
|
1541
|
-
qualityResult,
|
|
1542
|
-
canCapture,
|
|
1543
|
-
feedback
|
|
1544
|
-
};
|
|
1545
|
-
}
|
|
1546
|
-
// Method to get real-time quality feedback without full analysis
|
|
1547
|
-
getQuickQualityFeedback(video) {
|
|
1548
|
-
const canvas = document.createElement('canvas');
|
|
1549
|
-
canvas.width = Math.min(video.videoWidth, 640); // Reduced resolution for performance
|
|
1550
|
-
canvas.height = Math.min(video.videoHeight, 480);
|
|
1551
|
-
const ctx = canvas.getContext('2d');
|
|
1552
|
-
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
|
|
1553
|
-
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
|
1554
|
-
// Quick blur detection
|
|
1555
|
-
const blurMetrics = this.imageQualityService.detectBlur(imageData);
|
|
1556
|
-
// Quick reflection detection
|
|
1557
|
-
const reflectionMetrics = this.imageQualityService.detectReflections(imageData);
|
|
1558
|
-
// Quick focus measurement
|
|
1559
|
-
const focusMetrics = this.imageQualityService.measureFocus(imageData);
|
|
1560
|
-
let feedback = 'Analizando calidad...';
|
|
1561
|
-
if (!blurMetrics.isAcceptable) {
|
|
1562
|
-
feedback = 'Estabilice el dispositivo';
|
|
1563
|
-
}
|
|
1564
|
-
else if (reflectionMetrics.hasReflection) {
|
|
1565
|
-
feedback = 'Evite reflejos en el documento';
|
|
1566
|
-
}
|
|
1567
|
-
else if (!focusMetrics.isFocused) {
|
|
1568
|
-
feedback = 'Permitiendo enfoque automático...';
|
|
1569
|
-
}
|
|
1570
|
-
else {
|
|
1571
|
-
feedback = 'Calidad óptima';
|
|
1572
|
-
}
|
|
1573
|
-
return {
|
|
1574
|
-
hasBlur: !blurMetrics.isAcceptable,
|
|
1575
|
-
hasReflections: reflectionMetrics.hasReflection,
|
|
1576
|
-
isFocused: focusMetrics.isFocused,
|
|
1577
|
-
feedback
|
|
1578
|
-
};
|
|
1579
|
-
}
|
|
1580
1089
|
}
|
|
1581
1090
|
|
|
1582
1091
|
class ServiceContainer {
|
|
@@ -1590,12 +1099,7 @@ class ServiceContainer {
|
|
|
1590
1099
|
const logger = new LoggerService(config.debug);
|
|
1591
1100
|
const stateManager = new StateManagerService(eventBus);
|
|
1592
1101
|
const cameraService = new CameraService(logger, eventBus, config.preferredCamera);
|
|
1593
|
-
const detectionService = new DetectionService(logger, config.debug, config.useDocumentClassification
|
|
1594
|
-
minQualityScore: config.minQualityScore,
|
|
1595
|
-
minFocusScore: config.minFocusScore,
|
|
1596
|
-
minBlurScore: config.minBlurScore,
|
|
1597
|
-
maxReflectionScore: config.maxReflectionScore
|
|
1598
|
-
});
|
|
1102
|
+
const detectionService = new DetectionService(logger, config.debug, config.useDocumentClassification);
|
|
1599
1103
|
// Register services
|
|
1600
1104
|
this.services.set('eventBus', eventBus);
|
|
1601
1105
|
this.services.set('logger', logger);
|
|
@@ -1639,7 +1143,7 @@ class ServiceContainer {
|
|
|
1639
1143
|
}
|
|
1640
1144
|
}
|
|
1641
1145
|
|
|
1642
|
-
const myComponentCss = ":host{display:block;width:100%;height:100%;font-family:system-ui, -apple-system, sans-serif;color:#1a1a1a}.detector-container{display:flex;flex-direction:column;align-items:center;width:100%;height:100%}h1{font-size:24px;font-weight:500;color:#333;margin:0 0 24px 0}.video-container{position:relative;width:100%;height:100%;background:#333;border:1px solid #e0e0e0;border-radius:8px;overflow:hidden}video,.detection-overlay{position:absolute;width:100%;height:100%;border-radius:8px}video.mirror,.detection-overlay.mirror{transform:rotateY(180deg)}.detection-overlay{z-index:1;pointer-events:none}video{z-index:0}.detection-box{transition:opacity 0.2s ease}.status{margin-top:16px;font-size:12px;color:#666}.overlay-mask{position:absolute;top:0;left:0;width:100%;height:100%;z-index:10;display:block;pointer-events:none}.card-outline{position:absolute;top:var(--mask-center-y, 50%);left:var(--mask-center-x, 50%);transform:translate(-50%, -50%);width:var(--mask-width, 88%);height:var(--mask-height, 55%);border:none;border-radius:4px;background:transparent;opacity:0.8}.side{position:absolute;background:#999;transition:background-color 0.3s ease;border-radius:1px}.side-top.aligned{border-left-color:#28a745;border-top-color:#28a745}.side-right.aligned{border-right-color:#28a745;border-top-color:#28a745}.side-bottom.aligned{border-left-color:#28a745;border-bottom-color:#28a745}.side-left.aligned{border-right-color:#28a745;border-bottom-color:#28a745}.side-top{top:-3px;left:-3px;width:30px;height:30px;border-left:6px solid #ffffff;border-top:6px solid #ffffff;background:transparent}.side-right{top:-3px;right:-3px;width:30px;height:30px;border-right:6px solid #ffffff;border-top:6px solid #ffffff;background:transparent}.side-bottom{bottom:-3px;left:-3px;width:30px;height:30px;border-left:6px solid #ffffff;border-bottom:6px solid #ffffff;background:transparent}.side-left{bottom:-3px;right:-3px;width:30px;height:30px;border-right:6px solid #ffffff;border-bottom:6px solid #ffffff;background:transparent}.guide-text{position:absolute;top:50%;left:50%;transform:translate(-50%, -50%);color:#fff;font-size:14px;font-weight:600;text-align:center;white-space:normal;background:rgba(128, 128, 128, 0.8);padding:12px 20px;border-radius:20px;max-width:300px;z-index:20}.quality-indicator{position:absolute;top:20px;right:20px;display:flex;align-items:center;gap:8px;background:rgba(0, 0, 0, 0.8);padding:8px 12px;border-radius:20px;z-index:25;transition:all 0.3s ease}.quality-indicator.warning{background:rgba(255, 152, 0, 0.9)}.quality-indicator.good{background:rgba(76, 175, 80, 0.9)}.quality-score{color:#fff;font-size:12px;font-weight:600;min-width:30px;text-align:center}.quality-status{width:20px;height:20px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:14px;font-weight:bold;color:#fff;transition:all 0.3s ease}.quality-status.ready{background:#4CAF50}.quality-status.not-ready{background:#f44336}.card-outline.quality-warning{animation:qualityWarning 1s ease-in-out infinite alternate}@keyframes qualityWarning{0%{box-shadow:0 0 0 3px rgba(255, 152, 0, 0.6)}100%{box-shadow:0 0 0 6px rgba(255, 152, 0, 0.3)}}.capture-animation{position:absolute;top:0;left:0;width:100%;height:100%;background:#fff;opacity:0;z-index:30;pointer-events:none;animation:captureFlash 0.6s ease-out}@keyframes captureFlash{0%{opacity:0}15%{opacity:0.8}30%{opacity:0}45%{opacity:0.4}60%{opacity:0}100%{opacity:0}}.card-outline.capturing{animation:pulseGreen 0.6s ease-out}@keyframes pulseGreen{0%{border-color:#28a745;box-shadow:0 0 0 4px rgba(40, 167, 69, 0)}50%{border-color:#28a745;box-shadow:0 0 0 8px rgba(40, 167, 69, 0.6)}100%{border-color:#28a745;box-shadow:0 0 0 4px rgba(40, 167, 69, 0)}}.flip-animation{position:absolute;top:50%;left:50%;transform:translate(-50%, -50%);z-index:35;pointer-events:none;opacity:0;animation:showFlipInstruction 3s ease-in-out;display:flex;flex-direction:column;align-items:center;justify-content:center}.id-card-icon{width:80px;height:50px;background:linear-gradient(135deg, #6c757d 0%, #495057 100%);border-radius:8px;position:relative;animation:flipCard 2s ease-in-out infinite;box-shadow:0 4px 12px rgba(0, 0, 0, 0.3)}.id-card-icon::before{content:'';position:absolute;top:8px;left:8px;width:16px;height:12px;background:rgba(255, 255, 255, 0.9);border-radius:2px}.id-card-icon::after{content:'';position:absolute;top:25px;left:8px;width:64px;height:3px;background:rgba(255, 255, 255, 0.7);border-radius:1px;box-shadow:0 6px 0 rgba(255, 255, 255, 0.7), 0 12px 0 rgba(255, 255, 255, 0.7)}.flip-text{margin-top:12px;color:#fff;font-size:14px;font-weight:600;text-align:center;background:rgba(128, 128, 128, 0.8);padding:12px 20px;border-radius:20px;max-width:300px}@keyframes showFlipInstruction{0%{opacity:0;transform:translate(-50%, -50%) scale(0.8)}15%{opacity:1;transform:translate(-50%, -50%) scale(1)}85%{opacity:1;transform:translate(-50%, -50%) scale(1)}100%{opacity:0;transform:translate(-50%, -50%) scale(0.8)}}@keyframes flipCard{0%{transform:rotateY(0deg)}50%{transform:rotateY(180deg)}100%{transform:rotateY(360deg)}}.success-animation{position:absolute;top:50%;left:50%;transform:translate(-50%, -50%);z-index:35;pointer-events:none;opacity:0;animation:showSuccessMessage 3s ease-in-out;display:flex;flex-direction:column;align-items:center;justify-content:center}.check-icon{width:80px;height:80px;background:linear-gradient(135deg, #6c757d 0%, #495057 100%);border-radius:50%;position:relative;animation:bounceSuccess 0.6s ease-out;box-shadow:0 4px 16px rgba(108, 117, 125, 0.4);display:flex;align-items:center;justify-content:center}.check-icon::before{content:'';position:absolute;width:20px;height:35px;border:4px solid #fff;border-top:none;border-left:none;transform:rotate(45deg);animation:drawCheck 0.4s ease-out 0.2s both}.success-text{margin-top:16px;color:#fff;font-size:14px;font-weight:600;text-align:center;background:rgba(128, 128, 128, 0.8);padding:12px 20px;border-radius:20px;max-width:300px;animation:fadeInUp 0.5s ease-out 0.4s both}@keyframes showSuccessMessage{0%{opacity:0;transform:translate(-50%, -50%) scale(0.5)}15%{opacity:1;transform:translate(-50%, -50%) scale(1)}85%{opacity:1;transform:translate(-50%, -50%) scale(1)}100%{opacity:0;transform:translate(-50%, -50%) scale(0.9)}}@keyframes bounceSuccess{0%{transform:scale(0)}50%{transform:scale(1.1)}100%{transform:scale(1)}}@keyframes drawCheck{0%{width:0;height:0}50%{width:20px;height:0}100%{width:20px;height:35px}}@keyframes fadeInUp{0%{opacity:0;transform:translateY(20px)}100%{opacity:1;transform:translateY(0)}}.skip-button{position:absolute;bottom:20px;left:50%;transform:translateX(-50%);z-index:25;pointer-events:auto;background:#fff;color:#333;border:none;border-radius:25px;padding:12px 24px;font-size:14px;font-weight:600;cursor:pointer;transition:all 0.3s ease}.skip-button:hover{background:#f8f9fa}.skip-button:active{background:#e9ecef;transform:translateX(-50%) translateY(0)}.camera-controls{position:absolute;top:16px;right:16px;z-index:25;display:flex;gap:8px;pointer-events:auto}.camera-selector-button{height:32px;padding:0 10px;border:none;border-radius:8px;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(12px);color:#ffffff;font-size:12px;font-weight:500;cursor:pointer;transition:all 0.2s ease;display:flex;align-items:center;justify-content:center;border:1px solid rgba(255, 255, 255, 0.1);white-space:nowrap;min-width:fit-content}.camera-selector-button:hover{background:rgba(0, 0, 0, 0.8);border-color:rgba(255, 255, 255, 0.2);transform:translateY(-1px)}.camera-selector-button:active{transform:translateY(0);background:rgba(0, 0, 0, 0.9)}.camera-selector-button:disabled,.camera-selector-button.loading{opacity:0.7;cursor:not-allowed;pointer-events:none}.camera-selector-button:disabled:hover,.camera-selector-button.loading:hover{transform:none;background:rgba(0, 0, 0, 0.6);border-color:rgba(255, 255, 255, 0.1)}.button-spinner{width:16px;height:16px;border:2px solid rgba(255, 255, 255, 0.3);border-top:2px solid #ffffff;border-radius:50%;animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}.camera-selector-dropdown{position:absolute;top:56px;right:16px;z-index:30;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(20px);border-radius:12px;min-width:260px;max-width:300px;border:1px solid rgba(255, 255, 255, 0.1);overflow:hidden;pointer-events:auto;animation:slideInFromTop 0.3s ease-out}@keyframes slideInFromTop{0%{opacity:0;transform:translateY(-8px) scale(0.95)}100%{opacity:1;transform:translateY(0) scale(1)}}.camera-selector-header{display:flex;justify-content:space-between;align-items:center;padding:8px 12px;border-bottom:1px solid rgba(255, 255, 255, 0.1)}.camera-selector-header span{font-weight:500;color:#ffffff;font-size:13px;opacity:0.9}.close-selector{width:20px;height:20px;border:none;background:none;color:rgba(255, 255, 255, 0.7);font-size:16px;cursor:pointer;display:flex;align-items:center;justify-content:center;border-radius:4px;transition:all 0.2s ease}.close-selector:hover{background:rgba(255, 255, 255, 0.1);color:#ffffff}.camera-list{padding:4px 0;max-height:240px;overflow-y:auto}.camera-option{width:100%;padding:8px 12px;border:none;background:none;text-align:left;cursor:pointer;transition:all 0.2s ease;display:flex;justify-content:space-between;align-items:center;color:rgba(255, 255, 255, 0.9)}.camera-option:hover{background:rgba(255, 255, 255, 0.08)}.camera-option.selected{background:rgba(255, 255, 255, 0.12);color:#ffffff}.camera-label{font-size:13px;flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-right:8px;font-weight:400}.selected-indicator{font-size:14px;color:#ffffff;opacity:0.9}.device-info{padding:6px 12px;border-top:1px solid rgba(255, 255, 255, 0.1);background:rgba(255, 255, 255, 0.05)}.device-info small{color:rgba(255, 255, 255, 0.6);font-size:11px;text-transform:capitalize;font-weight:400}@media (max-width: 480px){.camera-controls{top:12px;right:12px;gap:6px}.camera-selector-button{height:36px;padding:0 12px;font-size:11px;border-radius:6px}.camera-selector-dropdown{right:12px;top:48px;min-width:240px;max-width:calc(100vw - 24px)}.camera-selector-header{padding:6px 10px}.camera-option{padding:6px 10px}.device-info{padding:4px 10px}}.watermark{position:absolute;bottom:12px;right:12px;z-index:15;pointer-events:none;opacity:0.7}.watermark img{height:24px;width:auto;filter:drop-shadow(0 1px 2px rgba(0, 0, 0, 0.3))}.component-status{position:absolute;top:16px;left:16px;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(20px);border-radius:12px;border:1px solid rgba(255, 255, 255, 0.1);display:flex;align-items:center;gap:6px;padding:6px 10px;z-index:40;height:32px;width:fit-content;animation:slideInFromTop 0.3s ease-out}.status-spinner{width:16px;height:16px;border:1px solid rgba(255, 255, 255, 0.3);border-top:1px solid #ffffff;border-radius:50%;animation:statusSpin 1s linear infinite;flex-shrink:0}.status-content{display:flex;flex-direction:column;gap:1px}.status-message{color:#ffffff;font-size:12px;font-weight:500;margin:0}.status-description{color:rgba(255, 255, 255, 0.7);font-size:10px;line-height:1.2;margin:0}@keyframes statusSpin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}@media (max-width: 480px){.component-status{top:12px;left:12px;padding:4px 8px;gap:4px;height:28px;border-radius:8px}.status-spinner{width:14px;height:14px}.status-message{font-size:11px}.status-description{font-size:9px}}.loading-overlay{position:absolute;top:0;left:0;width:100%;height:100%;background:rgba(0, 0, 0, 0.85);backdrop-filter:blur(4px);display:flex;flex-direction:column;align-items:center;justify-content:center;z-index:30;border-radius:8px}.loading-spinner{width:50px;height:50px;border:3px solid rgba(255, 255, 255, 0.15);border-top:3px solid #28a745;border-radius:50%;animation:spin 1s ease-in-out infinite;margin-bottom:16px}.loading-text{color:#ffffff;font-size:14px;font-weight:500;text-align:center;opacity:0.9;margin-bottom:4px}.loading-description{color:rgba(255, 255, 255, 0.7);font-size:12px;text-align:center;opacity:0.8}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}.performance-monitor{position:absolute;top:70px;left:16px;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(20px);border-radius:12px;border:1px solid rgba(255, 255, 255, 0.1);z-index:35;width:fit-content;animation:slideInFromLeft 0.3s ease-out;transition:all 0.3s ease}@keyframes slideInFromLeft{0%{opacity:0;transform:translateX(-20px) scale(0.95)}100%{opacity:1;transform:translateX(0) scale(1)}}.performance-expanded{padding:6px 10px}.metrics-row{display:flex;gap:8px;margin-bottom:4px}.metrics-row:last-child{margin-bottom:0}.metric-compact{display:flex;flex-direction:column;align-items:center;gap:2px;min-width:50px}.metric-label{font-size:9px;color:rgba(255, 255, 255, 0.6);font-weight:500;text-transform:uppercase;letter-spacing:0.3px}.metric-value{font-size:10px;font-weight:600;font-family:'Monaco', 'Menlo', 'Consolas', monospace;padding:2px 4px;border-radius:3px;text-align:center;white-space:nowrap}.metric-value.good{color:#4ade80;background:rgba(74, 222, 128, 0.1);border:1px solid rgba(74, 222, 128, 0.2)}.metric-value.warning{color:#fbbf24;background:rgba(251, 191, 36, 0.1);border:1px solid rgba(251, 191, 36, 0.2)}.metric-value.danger{color:#f87171;background:rgba(248, 113, 113, 0.1);border:1px solid rgba(248, 113, 113, 0.2)}@media (max-width: 480px){.performance-monitor{top:60px;left:12px;width:fit-content}.performance-expanded{padding:4px 8px}.metrics-row{gap:6px;margin-bottom:3px}.metric-compact{min-width:45px;gap:1px}.metric-label{font-size:8px}.metric-value{font-size:9px;padding:1px 3px}}@keyframes pulse{0%,100%{opacity:1}50%{opacity:0.7}}.metric-value.danger{animation:pulse 2s infinite}";
|
|
1146
|
+
const myComponentCss = ":host{display:block;width:100%;height:100%;font-family:system-ui, -apple-system, sans-serif;color:#1a1a1a}.detector-container{display:flex;flex-direction:column;align-items:center;width:100%;height:100%}h1{font-size:24px;font-weight:500;color:#333;margin:0 0 24px 0}.video-container{position:relative;width:100%;height:100%;background:#333;border:1px solid #e0e0e0;border-radius:8px;overflow:hidden}video,.detection-overlay{position:absolute;width:100%;height:100%;border-radius:8px}video.mirror,.detection-overlay.mirror{transform:rotateY(180deg)}.detection-overlay{z-index:1;pointer-events:none}video{z-index:0}.detection-box{transition:opacity 0.2s ease}.status{margin-top:16px;font-size:12px;color:#666}.overlay-mask{position:absolute;top:0;left:0;width:100%;height:100%;z-index:10;display:block;pointer-events:none}.card-outline{position:absolute;top:var(--mask-center-y, 50%);left:var(--mask-center-x, 50%);transform:translate(-50%, -50%);width:var(--mask-width, 88%);height:var(--mask-height, 55%);border:none;border-radius:4px;background:transparent;opacity:0.8}.side{position:absolute;background:#999;transition:background-color 0.3s ease;border-radius:1px}.side-top.aligned{border-left-color:#28a745;border-top-color:#28a745}.side-right.aligned{border-right-color:#28a745;border-top-color:#28a745}.side-bottom.aligned{border-left-color:#28a745;border-bottom-color:#28a745}.side-left.aligned{border-right-color:#28a745;border-bottom-color:#28a745}.side-top{top:-3px;left:-3px;width:30px;height:30px;border-left:6px solid #ffffff;border-top:6px solid #ffffff;background:transparent}.side-right{top:-3px;right:-3px;width:30px;height:30px;border-right:6px solid #ffffff;border-top:6px solid #ffffff;background:transparent}.side-bottom{bottom:-3px;left:-3px;width:30px;height:30px;border-left:6px solid #ffffff;border-bottom:6px solid #ffffff;background:transparent}.side-left{bottom:-3px;right:-3px;width:30px;height:30px;border-right:6px solid #ffffff;border-bottom:6px solid #ffffff;background:transparent}.guide-text{position:absolute;top:50%;left:50%;transform:translate(-50%, -50%);color:#fff;font-size:14px;font-weight:600;text-align:center;white-space:normal;background:rgba(128, 128, 128, 0.8);padding:12px 20px;border-radius:20px;max-width:300px;z-index:20}.quality-indicator{position:absolute;top:20px;right:20px;display:flex;align-items:center;gap:8px;background:rgba(0, 0, 0, 0.8);padding:8px 12px;border-radius:20px;z-index:25;transition:all 0.3s ease}.quality-indicator.warning{background:rgba(255, 152, 0, 0.9)}.quality-indicator.good{background:rgba(76, 175, 80, 0.9)}.quality-score{color:#fff;font-size:12px;font-weight:600;min-width:30px;text-align:center}.quality-status{width:20px;height:20px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:14px;font-weight:bold;color:#fff;transition:all 0.3s ease}.quality-status.ready{background:#4CAF50}.quality-status.not-ready{background:#f44336}.card-outline.quality-warning{animation:qualityWarning 1s ease-in-out infinite alternate}@keyframes qualityWarning{0%{box-shadow:0 0 0 3px rgba(255, 152, 0, 0.6)}100%{box-shadow:0 0 0 6px rgba(255, 152, 0, 0.3)}}.capture-animation{position:absolute;top:0;left:0;width:100%;height:100%;background:#fff;opacity:0;z-index:30;pointer-events:none;animation:captureFlash 0.6s ease-out}@keyframes captureFlash{0%{opacity:0}15%{opacity:0.8}30%{opacity:0}45%{opacity:0.4}60%{opacity:0}100%{opacity:0}}.card-outline.capturing{animation:pulseGreen 0.6s ease-out}@keyframes pulseGreen{0%{border-color:#28a745;box-shadow:0 0 0 4px rgba(40, 167, 69, 0)}50%{border-color:#28a745;box-shadow:0 0 0 8px rgba(40, 167, 69, 0.6)}100%{border-color:#28a745;box-shadow:0 0 0 4px rgba(40, 167, 69, 0)}}.flip-animation{position:absolute;top:50%;left:50%;transform:translate(-50%, -50%);z-index:35;pointer-events:none;opacity:0;animation:showFlipInstruction 3s ease-in-out;display:flex;flex-direction:column;align-items:center;justify-content:center}.id-card-icon{width:80px;height:50px;background:linear-gradient(135deg, #6c757d 0%, #495057 100%);border-radius:8px;position:relative;animation:flipCard 2s ease-in-out infinite;box-shadow:0 4px 12px rgba(0, 0, 0, 0.3)}.id-card-icon::before{content:'';position:absolute;top:8px;left:8px;width:16px;height:12px;background:rgba(255, 255, 255, 0.9);border-radius:2px}.id-card-icon::after{content:'';position:absolute;top:25px;left:8px;width:64px;height:3px;background:rgba(255, 255, 255, 0.7);border-radius:1px;box-shadow:0 6px 0 rgba(255, 255, 255, 0.7), 0 12px 0 rgba(255, 255, 255, 0.7)}.flip-text{margin-top:12px;color:#fff;font-size:14px;font-weight:600;text-align:center;background:rgba(128, 128, 128, 0.8);padding:12px 20px;border-radius:20px;max-width:300px}@keyframes showFlipInstruction{0%{opacity:0;transform:translate(-50%, -50%) scale(0.8)}15%{opacity:1;transform:translate(-50%, -50%) scale(1)}85%{opacity:1;transform:translate(-50%, -50%) scale(1)}100%{opacity:0;transform:translate(-50%, -50%) scale(0.8)}}@keyframes flipCard{0%{transform:rotateY(0deg)}50%{transform:rotateY(180deg)}100%{transform:rotateY(360deg)}}.success-animation{position:absolute;top:50%;left:50%;transform:translate(-50%, -50%);z-index:35;pointer-events:none;opacity:0;animation:showSuccessMessage 3s ease-in-out;display:flex;flex-direction:column;align-items:center;justify-content:center}.check-icon{width:80px;height:80px;background:linear-gradient(135deg, #6c757d 0%, #495057 100%);border-radius:50%;position:relative;animation:bounceSuccess 0.6s ease-out;box-shadow:0 4px 16px rgba(108, 117, 125, 0.4);display:flex;align-items:center;justify-content:center}.check-icon::before{content:'';position:absolute;width:20px;height:35px;border:4px solid #fff;border-top:none;border-left:none;transform:rotate(45deg);animation:drawCheck 0.4s ease-out 0.2s both}.success-text{margin-top:16px;color:#fff;font-size:14px;font-weight:600;text-align:center;background:rgba(128, 128, 128, 0.8);padding:12px 20px;border-radius:20px;max-width:300px;animation:fadeInUp 0.5s ease-out 0.4s both}@keyframes showSuccessMessage{0%{opacity:0;transform:translate(-50%, -50%) scale(0.5)}15%{opacity:1;transform:translate(-50%, -50%) scale(1)}85%{opacity:1;transform:translate(-50%, -50%) scale(1)}100%{opacity:0;transform:translate(-50%, -50%) scale(0.9)}}@keyframes bounceSuccess{0%{transform:scale(0)}50%{transform:scale(1.1)}100%{transform:scale(1)}}@keyframes drawCheck{0%{width:0;height:0}50%{width:20px;height:0}100%{width:20px;height:35px}}@keyframes fadeInUp{0%{opacity:0;transform:translateY(20px)}100%{opacity:1;transform:translateY(0)}}.skip-button{position:absolute;bottom:20px;left:50%;transform:translateX(-50%);z-index:25;pointer-events:auto;background:#fff;color:#333;border:none;border-radius:25px;padding:12px 24px;font-size:14px;font-weight:600;cursor:pointer;transition:all 0.3s ease}.skip-button:hover{background:#f8f9fa}.skip-button:active{background:#e9ecef;transform:translateX(-50%) translateY(0)}.camera-controls{position:absolute;top:16px;right:16px;z-index:25;display:flex;gap:8px;pointer-events:auto}.camera-selector-button{height:32px;padding:0 10px;border:none;border-radius:8px;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(12px);color:#ffffff;font-size:12px;font-weight:500;cursor:pointer;transition:all 0.2s ease;display:flex;align-items:center;justify-content:center;border:1px solid rgba(255, 255, 255, 0.1);white-space:nowrap;min-width:fit-content}.camera-selector-button:hover{background:rgba(0, 0, 0, 0.8);border-color:rgba(255, 255, 255, 0.2);transform:translateY(-1px)}.camera-selector-button:active{transform:translateY(0);background:rgba(0, 0, 0, 0.9)}.camera-selector-button:disabled,.camera-selector-button.loading{opacity:0.7;cursor:not-allowed;pointer-events:none}.camera-selector-button:disabled:hover,.camera-selector-button.loading:hover{transform:none;background:rgba(0, 0, 0, 0.6);border-color:rgba(255, 255, 255, 0.1)}.button-spinner{width:16px;height:16px;border:2px solid rgba(255, 255, 255, 0.3);border-top:2px solid #ffffff;border-radius:50%;animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}.camera-selector-dropdown{position:absolute;top:56px;right:16px;z-index:30;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(20px);border-radius:12px;min-width:260px;max-width:300px;border:1px solid rgba(255, 255, 255, 0.1);overflow:hidden;pointer-events:auto;animation:slideInFromTop 0.3s ease-out}@keyframes slideInFromTop{0%{opacity:0;transform:translateY(-8px) scale(0.95)}100%{opacity:1;transform:translateY(0) scale(1)}}.camera-selector-header{display:flex;justify-content:space-between;align-items:center;padding:8px 12px;border-bottom:1px solid rgba(255, 255, 255, 0.1)}.camera-selector-header span{font-weight:500;color:#ffffff;font-size:13px;opacity:0.9}.close-selector{width:20px;height:20px;border:none;background:none;color:rgba(255, 255, 255, 0.7);font-size:16px;cursor:pointer;display:flex;align-items:center;justify-content:center;border-radius:4px;transition:all 0.2s ease}.close-selector:hover{background:rgba(255, 255, 255, 0.1);color:#ffffff}.camera-list{padding:4px 0;max-height:240px;overflow-y:auto}.camera-option{width:100%;padding:8px 12px;border:none;background:none;text-align:left;cursor:pointer;transition:all 0.2s ease;display:flex;justify-content:space-between;align-items:center;color:rgba(255, 255, 255, 0.9)}.camera-option:hover{background:rgba(255, 255, 255, 0.08)}.camera-option.selected{background:rgba(255, 255, 255, 0.12);color:#ffffff}.camera-label{font-size:13px;flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-right:8px;font-weight:400}.selected-indicator{font-size:14px;color:#ffffff;opacity:0.9}.device-info{padding:6px 12px;border-top:1px solid rgba(255, 255, 255, 0.1);background:rgba(255, 255, 255, 0.05)}.device-info small{color:rgba(255, 255, 255, 0.6);font-size:11px;text-transform:capitalize;font-weight:400}@media (max-width: 480px){.camera-controls{top:12px;right:12px;gap:6px}.camera-selector-button{height:36px;padding:0 12px;font-size:11px;border-radius:6px}.camera-selector-dropdown{right:12px;top:48px;min-width:240px;max-width:calc(100vw - 24px)}.camera-selector-header{padding:6px 10px}.camera-option{padding:6px 10px}.device-info{padding:4px 10px}}.watermark{position:absolute;bottom:12px;right:12px;z-index:15;pointer-events:none;opacity:0.7}.watermark img{height:24px;width:auto;filter:drop-shadow(0 1px 2px rgba(0, 0, 0, 0.3))}.component-status{position:absolute;top:16px;left:16px;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(20px);border-radius:12px;border:1px solid rgba(255, 255, 255, 0.1);display:flex;align-items:center;gap:6px;padding:6px 10px;z-index:40;height:32px;width:fit-content;animation:slideInFromTop 0.3s ease-out}.status-spinner{width:16px;height:16px;border:1px solid rgba(255, 255, 255, 0.3);border-top:1px solid #ffffff;border-radius:50%;animation:statusSpin 1s linear infinite;flex-shrink:0}.status-content{display:flex;flex-direction:column;gap:1px}.status-message{color:#ffffff;font-size:12px;font-weight:500;margin:0}.status-description{color:rgba(255, 255, 255, 0.7);font-size:10px;line-height:1.2;margin:0}@keyframes statusSpin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}@media (max-width: 480px){.component-status{top:12px;left:12px;padding:4px 8px;gap:4px;height:28px;border-radius:8px}.status-spinner{width:14px;height:14px}.status-message{font-size:11px}.status-description{font-size:9px}}.loading-overlay{position:absolute;top:0;left:0;width:100%;height:100%;background:rgba(0, 0, 0, 0.85);backdrop-filter:blur(4px);display:flex;flex-direction:column;align-items:center;justify-content:center;z-index:30;border-radius:8px}.loading-spinner{width:50px;height:50px;border:3px solid rgba(255, 255, 255, 0.15);border-top:3px solid #28a745;border-radius:50%;animation:spin 1s ease-in-out infinite;margin-bottom:16px}.loading-text{color:#ffffff;font-size:14px;font-weight:500;text-align:center;opacity:0.9;margin-bottom:4px}.loading-description{color:rgba(255, 255, 255, 0.7);font-size:12px;text-align:center;opacity:0.8}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}.performance-monitor{position:absolute;top:70px;left:16px;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(20px);border-radius:12px;border:1px solid rgba(255, 255, 255, 0.1);z-index:35;width:fit-content;animation:slideInFromLeft 0.3s ease-out;transition:all 0.3s ease}@keyframes slideInFromLeft{0%{opacity:0;transform:translateX(-20px) scale(0.95)}100%{opacity:1;transform:translateX(0) scale(1)}}.performance-expanded{padding:6px 10px}.metrics-row{display:flex;gap:8px;margin-bottom:4px}.metrics-row:last-child{margin-bottom:0}.metric-compact{display:flex;flex-direction:column;align-items:center;gap:2px;min-width:50px}.metric-label{font-size:9px;color:rgba(255, 255, 255, 0.6);font-weight:500;text-transform:uppercase;letter-spacing:0.3px}.metric-value{font-size:10px;font-weight:600;font-family:'Monaco', 'Menlo', 'Consolas', monospace;padding:2px 4px;border-radius:3px;text-align:center;white-space:nowrap}.metric-value.good{color:#4ade80;background:rgba(74, 222, 128, 0.1);border:1px solid rgba(74, 222, 128, 0.2)}.metric-value.warning{color:#fbbf24;background:rgba(251, 191, 36, 0.1);border:1px solid rgba(251, 191, 36, 0.2)}.metric-value.danger{color:#f87171;background:rgba(248, 113, 113, 0.1);border:1px solid rgba(248, 113, 113, 0.2)}@media (max-width: 480px){.performance-monitor{top:60px;left:12px;width:fit-content}.performance-expanded{padding:4px 8px}.metrics-row{gap:6px;margin-bottom:3px}.metric-compact{min-width:45px;gap:1px}.metric-label{font-size:8px}.metric-value{font-size:9px;padding:1px 3px}}.quality-monitor{position:absolute;top:200px;left:16px;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(20px);border-radius:12px;border:1px solid rgba(255, 255, 255, 0.1);z-index:35;width:fit-content;animation:slideInFromLeft 0.3s ease-out;transition:all 0.3s ease}.quality-text{padding:6px 10px;font-size:11px;color:white;font-family:'Monaco', 'Menlo', 'Consolas', monospace;line-height:1.4}.quality-fail{color:#ff9999}@keyframes pulse{0%,100%{opacity:1}50%{opacity:0.7}}.metric-value.danger{animation:pulse 2s infinite}@media (max-width: 480px){.quality-monitor{top:160px;left:12px}.quality-text{padding:4px 8px;font-size:10px}}";
|
|
1643
1147
|
|
|
1644
1148
|
const JaakStamps = class {
|
|
1645
1149
|
constructor(hostRef) {
|
|
@@ -1649,18 +1153,12 @@ const JaakStamps = class {
|
|
|
1649
1153
|
}
|
|
1650
1154
|
get el() { return index.getElement(this); }
|
|
1651
1155
|
debug = false;
|
|
1652
|
-
alignmentTolerance =
|
|
1653
|
-
maskSize =
|
|
1654
|
-
cropMargin =
|
|
1156
|
+
alignmentTolerance = 20;
|
|
1157
|
+
maskSize = 80;
|
|
1158
|
+
cropMargin = 20;
|
|
1655
1159
|
useDocumentClassification = false;
|
|
1656
1160
|
preferredCamera = 'auto';
|
|
1657
1161
|
captureDelay = 1000;
|
|
1658
|
-
enableQualityValidation = true;
|
|
1659
|
-
qualityThreshold = 60;
|
|
1660
|
-
minQualityScore = 45;
|
|
1661
|
-
minFocusScore = 16;
|
|
1662
|
-
minBlurScore = 22;
|
|
1663
|
-
maxReflectionScore = 15;
|
|
1664
1162
|
captureCompleted;
|
|
1665
1163
|
isReady;
|
|
1666
1164
|
// State derived from services
|
|
@@ -1679,12 +1177,6 @@ const JaakStamps = class {
|
|
|
1679
1177
|
type: 'initializing',
|
|
1680
1178
|
isInitialized: false
|
|
1681
1179
|
};
|
|
1682
|
-
qualityFeedback = {
|
|
1683
|
-
message: 'Analizando calidad...',
|
|
1684
|
-
score: 0,
|
|
1685
|
-
hasIssues: false,
|
|
1686
|
-
canCapture: false
|
|
1687
|
-
};
|
|
1688
1180
|
performanceData = {
|
|
1689
1181
|
fps: 0,
|
|
1690
1182
|
inferenceTime: 0,
|
|
@@ -1753,13 +1245,7 @@ const JaakStamps = class {
|
|
|
1753
1245
|
cropMargin: this.cropMargin,
|
|
1754
1246
|
useDocumentClassification: this.useDocumentClassification,
|
|
1755
1247
|
preferredCamera: this.preferredCamera,
|
|
1756
|
-
captureDelay: this.captureDelay
|
|
1757
|
-
enableQualityValidation: this.enableQualityValidation,
|
|
1758
|
-
qualityThreshold: this.qualityThreshold,
|
|
1759
|
-
minQualityScore: this.minQualityScore,
|
|
1760
|
-
minFocusScore: this.minFocusScore,
|
|
1761
|
-
minBlurScore: this.minBlurScore,
|
|
1762
|
-
maxReflectionScore: this.maxReflectionScore
|
|
1248
|
+
captureDelay: this.captureDelay
|
|
1763
1249
|
};
|
|
1764
1250
|
this.serviceContainer = new ServiceContainer(config);
|
|
1765
1251
|
this.logger = this.serviceContainer.getLogger();
|
|
@@ -1819,26 +1305,6 @@ const JaakStamps = class {
|
|
|
1819
1305
|
this.logger.warn(`Propiedad captureDelay inválida. Valor: ${this.captureDelay}, esperado: 0-10000. Usando valor por defecto: 1000`);
|
|
1820
1306
|
this.captureDelay = 1000;
|
|
1821
1307
|
}
|
|
1822
|
-
if (this.qualityThreshold < 0 || this.qualityThreshold > 100) {
|
|
1823
|
-
this.logger.warn(`Propiedad qualityThreshold inválida. Valor: ${this.qualityThreshold}, esperado: 0-100. Usando valor por defecto: 60`);
|
|
1824
|
-
this.qualityThreshold = 60;
|
|
1825
|
-
}
|
|
1826
|
-
if (this.minQualityScore < 0 || this.minQualityScore > 100) {
|
|
1827
|
-
this.logger.warn(`Propiedad minQualityScore inválida. Valor: ${this.minQualityScore}, esperado: 0-100. Usando valor por defecto: 45`);
|
|
1828
|
-
this.minQualityScore = 45;
|
|
1829
|
-
}
|
|
1830
|
-
if (this.minFocusScore < 0 || this.minFocusScore > 100) {
|
|
1831
|
-
this.logger.warn(`Propiedad minFocusScore inválida. Valor: ${this.minFocusScore}, esperado: 0-100. Usando valor por defecto: 16`);
|
|
1832
|
-
this.minFocusScore = 16;
|
|
1833
|
-
}
|
|
1834
|
-
if (this.minBlurScore < 0 || this.minBlurScore > 200) {
|
|
1835
|
-
this.logger.warn(`Propiedad minBlurScore inválida. Valor: ${this.minBlurScore}, esperado: 0-200. Usando valor por defecto: 22`);
|
|
1836
|
-
this.minBlurScore = 22;
|
|
1837
|
-
}
|
|
1838
|
-
if (this.maxReflectionScore < 0 || this.maxReflectionScore > 100) {
|
|
1839
|
-
this.logger.warn(`Propiedad maxReflectionScore inválida. Valor: ${this.maxReflectionScore}, esperado: 0-100. Usando valor por defecto: 15`);
|
|
1840
|
-
this.maxReflectionScore = 15;
|
|
1841
|
-
}
|
|
1842
1308
|
}
|
|
1843
1309
|
async loadOnnxRuntime() {
|
|
1844
1310
|
if (!window.ort) {
|
|
@@ -2085,63 +1551,6 @@ const JaakStamps = class {
|
|
|
2085
1551
|
coordinates: { x, y }
|
|
2086
1552
|
};
|
|
2087
1553
|
}
|
|
2088
|
-
async getImageQuality() {
|
|
2089
|
-
if (!this.videoRef || !this.detectionService.isModelLoaded()) {
|
|
2090
|
-
return null;
|
|
2091
|
-
}
|
|
2092
|
-
try {
|
|
2093
|
-
const qualityResult = this.detectionService.validateImageQuality(this.videoRef);
|
|
2094
|
-
return {
|
|
2095
|
-
qualityScore: qualityResult.qualityScore,
|
|
2096
|
-
overallQuality: qualityResult.overallQuality,
|
|
2097
|
-
issues: qualityResult.issues,
|
|
2098
|
-
recommendations: qualityResult.recommendations,
|
|
2099
|
-
canCapture: this.detectionService.isImageQualityAcceptable(qualityResult)
|
|
2100
|
-
};
|
|
2101
|
-
}
|
|
2102
|
-
catch (error) {
|
|
2103
|
-
this.logger.error('Error al analizar calidad de imagen:', error);
|
|
2104
|
-
return null;
|
|
2105
|
-
}
|
|
2106
|
-
}
|
|
2107
|
-
async setQualityThresholds(thresholds) {
|
|
2108
|
-
// Validate thresholds
|
|
2109
|
-
if (thresholds.minQualityScore !== undefined && (thresholds.minQualityScore < 0 || thresholds.minQualityScore > 100)) {
|
|
2110
|
-
throw new Error('minQualityScore must be between 0 and 100');
|
|
2111
|
-
}
|
|
2112
|
-
if (thresholds.minFocusScore !== undefined && (thresholds.minFocusScore < 0 || thresholds.minFocusScore > 100)) {
|
|
2113
|
-
throw new Error('minFocusScore must be between 0 and 100');
|
|
2114
|
-
}
|
|
2115
|
-
if (thresholds.minBlurScore !== undefined && (thresholds.minBlurScore < 0 || thresholds.minBlurScore > 200)) {
|
|
2116
|
-
throw new Error('minBlurScore must be between 0 and 200');
|
|
2117
|
-
}
|
|
2118
|
-
if (thresholds.maxReflectionScore !== undefined && (thresholds.maxReflectionScore < 0 || thresholds.maxReflectionScore > 100)) {
|
|
2119
|
-
throw new Error('maxReflectionScore must be between 0 and 100');
|
|
2120
|
-
}
|
|
2121
|
-
// Update component properties
|
|
2122
|
-
if (thresholds.minQualityScore !== undefined)
|
|
2123
|
-
this.minQualityScore = thresholds.minQualityScore;
|
|
2124
|
-
if (thresholds.minFocusScore !== undefined)
|
|
2125
|
-
this.minFocusScore = thresholds.minFocusScore;
|
|
2126
|
-
if (thresholds.minBlurScore !== undefined)
|
|
2127
|
-
this.minBlurScore = thresholds.minBlurScore;
|
|
2128
|
-
if (thresholds.maxReflectionScore !== undefined)
|
|
2129
|
-
this.maxReflectionScore = thresholds.maxReflectionScore;
|
|
2130
|
-
// Update detection service
|
|
2131
|
-
this.detectionService.updateQualityThresholds(thresholds);
|
|
2132
|
-
return {
|
|
2133
|
-
success: true,
|
|
2134
|
-
thresholds: this.detectionService.getQualityThresholds()
|
|
2135
|
-
};
|
|
2136
|
-
}
|
|
2137
|
-
async getQualityThresholds() {
|
|
2138
|
-
return {
|
|
2139
|
-
minQualityScore: this.minQualityScore,
|
|
2140
|
-
minFocusScore: this.minFocusScore,
|
|
2141
|
-
minBlurScore: this.minBlurScore,
|
|
2142
|
-
maxReflectionScore: this.maxReflectionScore
|
|
2143
|
-
};
|
|
2144
|
-
}
|
|
2145
1554
|
// DETECTION METHODS
|
|
2146
1555
|
async startDetection() {
|
|
2147
1556
|
this.logger.state('INICIANDO_DETECCION');
|
|
@@ -2243,36 +1652,8 @@ const JaakStamps = class {
|
|
|
2243
1652
|
// Measure preprocessing and inference time
|
|
2244
1653
|
const inferenceStartTime = performance.now();
|
|
2245
1654
|
const inputTensor = this.detectionService.preprocess(this.videoRef);
|
|
2246
|
-
//
|
|
2247
|
-
|
|
2248
|
-
let qualityResult = null;
|
|
2249
|
-
let canCapture = true;
|
|
2250
|
-
if (this.enableQualityValidation) {
|
|
2251
|
-
// Use enhanced detection method with quality validation
|
|
2252
|
-
const enhancedResult = await this.detectionService.runInferenceWithQuality(inputTensor, this.videoRef);
|
|
2253
|
-
detections = enhancedResult.detections;
|
|
2254
|
-
qualityResult = enhancedResult.qualityResult;
|
|
2255
|
-
canCapture = enhancedResult.canCapture;
|
|
2256
|
-
// Update quality feedback state
|
|
2257
|
-
this.qualityFeedback = {
|
|
2258
|
-
message: enhancedResult.feedback,
|
|
2259
|
-
score: qualityResult.qualityScore,
|
|
2260
|
-
hasIssues: qualityResult.issues.length > 0,
|
|
2261
|
-
canCapture: canCapture
|
|
2262
|
-
};
|
|
2263
|
-
}
|
|
2264
|
-
else {
|
|
2265
|
-
// Standard detection without quality validation
|
|
2266
|
-
detections = await this.detectionService.runInference(inputTensor);
|
|
2267
|
-
// Quick quality feedback for real-time display
|
|
2268
|
-
const quickFeedback = this.detectionService.getQuickQualityFeedback(this.videoRef);
|
|
2269
|
-
this.qualityFeedback = {
|
|
2270
|
-
message: quickFeedback.feedback,
|
|
2271
|
-
score: 0, // Not calculated in quick mode
|
|
2272
|
-
hasIssues: quickFeedback.hasBlur || quickFeedback.hasReflections || !quickFeedback.isFocused,
|
|
2273
|
-
canCapture: true // Allow capture without strict validation
|
|
2274
|
-
};
|
|
2275
|
-
}
|
|
1655
|
+
// Standard detection without quality validation
|
|
1656
|
+
const detections = await this.detectionService.runInference(inputTensor);
|
|
2276
1657
|
const inferenceTime = performance.now() - inferenceStartTime;
|
|
2277
1658
|
// Record ONNX performance metrics
|
|
2278
1659
|
if (this.debug) {
|
|
@@ -2280,7 +1661,7 @@ const JaakStamps = class {
|
|
|
2280
1661
|
}
|
|
2281
1662
|
// Update best score logic
|
|
2282
1663
|
if (this.startTime && Date.now() - this.startTime < 5000) {
|
|
2283
|
-
detections.forEach(detection => {
|
|
1664
|
+
detections.forEach((detection) => {
|
|
2284
1665
|
const isWellPositioned = this.detectionService.isCardInFrame(detection);
|
|
2285
1666
|
const effectiveScore = isWellPositioned ? detection.score * 1.2 : detection.score;
|
|
2286
1667
|
if (effectiveScore > captureState.bestScore) {
|
|
@@ -2366,7 +1747,7 @@ const JaakStamps = class {
|
|
|
2366
1747
|
availableCameras: [],
|
|
2367
1748
|
selectedCameraId: null,
|
|
2368
1749
|
deviceType: 'desktop'};
|
|
2369
|
-
return (index.h("div", { key: '
|
|
1750
|
+
return (index.h("div", { key: '7af79a0ff25b51790a9477cd7b338a0584172fc2', class: "detector-container" }, index.h("div", { key: 'ebf6a3811e0e599f29e98325b975a645e3bfb5e0', class: "video-container" }, index.h("video", { key: '5208fc9574fa78b1149be5839bc26d04bf9bcdfc', ref: el => this.videoRef = el, autoplay: true, muted: true, playsinline: true, class: this.shouldMirrorVideo ? 'mirror' : '', style: { display: captureState.isVideoActive ? 'block' : 'none' } }), index.h("div", { key: 'b507c786eccbd21acdd884ef50bc6ec280b9d437', ref: el => this.detectionContainer = el, class: `detection-overlay ${this.shouldMirrorVideo ? 'mirror' : ''}` }, this.debug && this.detectionBoxes.map((box, index$1) => (index.h("div", { key: index$1, class: "detection-box", style: {
|
|
2370
1751
|
position: 'absolute',
|
|
2371
1752
|
left: `${box.x}px`,
|
|
2372
1753
|
top: `${box.y}px`,
|
|
@@ -2375,9 +1756,7 @@ const JaakStamps = class {
|
|
|
2375
1756
|
border: '2px solid #32406C',
|
|
2376
1757
|
pointerEvents: 'none',
|
|
2377
1758
|
boxSizing: 'border-box'
|
|
2378
|
-
} })))), this.isMaskReady && (index.h("div", { key: '
|
|
2379
|
-
this.qualityFeedback.message :
|
|
2380
|
-
'Alinee su identificación con el marco')), this.enableQualityValidation && this.hasDocumentDetected && captureState.isVideoActive && !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (index.h("div", { key: '04afa661c1d988d9bedb48c78672de233dd49f9b', class: `quality-indicator ${this.qualityFeedback.hasIssues ? 'warning' : 'good'}` }, index.h("div", { key: 'bc789e19894de1d3dfd4a232e05e612d804e0e6c', class: "quality-score" }, this.qualityFeedback.score > 0 ? `${this.qualityFeedback.score}%` : ''), index.h("div", { key: '0750f5e5dfdb7a7c054d01965146d34a5bb4485a', class: `quality-status ${this.qualityFeedback.canCapture ? 'ready' : 'not-ready'}` }, this.qualityFeedback.canCapture ? '✓' : '!')))), captureState.step === 'back' && !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (index.h("button", { key: 'cbe391726b5d62acbb466c36b135e0556fc488f7', class: "skip-button", onClick: () => this.skipBackCapture(), type: "button" }, "Saltar reverso")), captureState.isVideoActive && (index.h("div", { key: '929f8fe054b30e3c30bf066319e957a633fa587c', class: "camera-controls" }, index.h("button", { key: 'b4f68dd3a6865cba49c3f5d38c43874b7b9b364d', class: `camera-selector-button ${this.isSwitchingCamera ? 'loading' : ''}`, onClick: () => this.toggleCameraSelector(), type: "button", title: "Seleccionar c\u00E1mara", disabled: this.isSwitchingCamera }, this.isSwitchingCamera ? (index.h("div", { class: "button-spinner" })) : ('Cámaras')))), this.showCameraSelector && cameraInfo.availableCameras.length > 0 && (index.h("div", { key: 'c9315ff70704fdc265af07bf765b6247814563e6', class: "camera-selector-dropdown" }, index.h("div", { key: 'f4950e7e3c3e279c48310a4049550f3143fb4d3f', class: "camera-selector-header" }, index.h("span", { key: '4cfacf909549b0e18bd8ac6e54e127ce09b75335' }, "Seleccionar C\u00E1mara"), index.h("button", { key: '13d7679c5235f41f0dbc38b9f397483cf4ec9ffd', class: "close-selector", onClick: () => this.toggleCameraSelector(), type: "button" }, "\u00D7")), index.h("div", { key: '6c97cb8ba9486c33a633901d9dd49765f87d92ed', class: "camera-list" }, cameraInfo.availableCameras.map((camera) => (index.h("button", { key: camera.id, class: `camera-option ${cameraInfo.selectedCameraId === camera.id ? 'selected' : ''}`, onClick: () => this.handleCameraSwitch(camera.id), type: "button" }, index.h("span", { class: "camera-label" }, camera.label || `Cámara ${cameraInfo.availableCameras.indexOf(camera) + 1}`), cameraInfo.selectedCameraId === camera.id && (index.h("span", { class: "selected-indicator" }, "\u2713")))))), index.h("div", { key: '35796316fc950b8491f6167303b549b098abbf58', class: "device-info" }, index.h("small", { key: '83c59a8cb0c0b67a37163fe75c1f1f383fcb00f4' }, "Dispositivo: ", cameraInfo.deviceType)))))), captureState.isCapturing && (index.h("div", { key: 'be5b316abb21c06cf5bb69ed090d43d45537a9e6', class: "capture-animation" })), captureState.showFlipAnimation && (index.h("div", { key: '43e64fcc325c7c96871a99838c5fa094c6774af2', class: "flip-animation" }, index.h("div", { key: '39223d9b593716e76912ade58b556c7212a7e05f', class: "id-card-icon" }), index.h("div", { key: '79463b43724c3c56c45bd5b41337117d7d415618', class: "flip-text" }, "\u00A1Voltea tu identificaci\u00F3n!"))), captureState.showSuccessAnimation && (index.h("div", { key: '7be8ca4d0bd59b1e4127254ff68e41321aa387cc', class: "success-animation" }, index.h("div", { key: '03a3e625f622c546a3016f85e029f9ea165f65c3', class: "check-icon" }), index.h("div", { key: '9c63faad517dad4b2ede8ec5a0998ad0b5c807d5', class: "success-text" }, "\u00A1Proceso completado!"))), index.h("div", { key: '94aa38c67fdaf328e74a2c1ec476987bf8e483d5', class: `component-status status-${this.currentStatus.type}` }, (this.currentStatus.type === 'loading' || this.currentStatus.type === 'initializing') && (index.h("div", { key: '52a460f2fe11505158331cc06d7121db80bdfed9', class: "status-spinner" })), index.h("div", { key: '1eabfed00df31ff30fdf818f3d33cd837a7f31a5', class: "status-content" }, index.h("div", { key: '4bdbadf18fc48f2fad5dafd9ddf4a78f0a81dbf5', class: "status-message" }, this.currentStatus.message), this.currentStatus.description && (index.h("div", { key: '14c41346fc9fc306315cdda1c54ec1e828c38f62', class: "status-description" }, this.currentStatus.description)))), this.debug && (index.h("div", { key: '21fb0ca08547143648097f2aa4f3eb3ade73bce1', class: "performance-monitor" }, index.h("div", { key: '88c990f42b5c87c9bc971acd228fc84bacec861b', class: "performance-expanded" }, index.h("div", { key: '351215f4b8ab202b42a4c9b0bfa8665485469f64', class: "metrics-row" }, index.h("div", { key: '28a2c5386ba99850f7dc41add6fb4ef073a6b8cb', class: "metric-compact" }, index.h("span", { key: '7a2ab758f9baf1264cd4ad70ff30f42a35c41065', class: "metric-label" }, "FPS"), index.h("span", { key: '1c4370d016f2bdcf61275b4869bddbd8109a7b2a', class: `metric-value ${this.performanceData.fps < 15 ? 'warning' : this.performanceData.fps < 10 ? 'danger' : 'good'}` }, this.performanceData.fps)), index.h("div", { key: 'e79c27b4cbad3e70f2df917a5147dd125d51304d', class: "metric-compact" }, index.h("span", { key: 'aee4c178ae751cfe94735ef9fcb78f273f9cb885', class: "metric-label" }, "MEM"), index.h("span", { key: '823e98dec413968ee5080b29839729e6ac9f91d2', class: `metric-value ${this.performanceData.memoryUsage > 100 ? 'warning' : this.performanceData.memoryUsage > 200 ? 'danger' : 'good'}` }, this.performanceData.memoryUsage, "MB"))), index.h("div", { key: 'e29037f684c82579a711aff022060c97ab9d93cf', class: "metrics-row" }, index.h("div", { key: '2917f3c4b026e3106fad741944e582aff36e10e0', class: "metric-compact" }, index.h("span", { key: 'e8783e8e2b18b29b37342eea376a75f257a46850', class: "metric-label" }, "INF"), index.h("span", { key: '30cdc1db3189135bc772aab5c31e081294ebd431', class: `metric-value ${this.performanceData.inferenceTime > 100 ? 'warning' : this.performanceData.inferenceTime > 200 ? 'danger' : 'good'}` }, this.performanceData.inferenceTime, "ms")), index.h("div", { key: 'c481ec26394e35efe38047e5fa1b835e520f49b8', class: "metric-compact" }, index.h("span", { key: 'c806cae91f3fcad896bd6053b6a0391ab518ab6f', class: "metric-label" }, "FRAME"), index.h("span", { key: 'af9996e2093c918ea903d036e637905baeef1aa2', class: `metric-value ${this.performanceData.frameProcessingTime > 50 ? 'warning' : this.performanceData.frameProcessingTime > 100 ? 'danger' : 'good'}` }, this.performanceData.frameProcessingTime, "ms"))), index.h("div", { key: '48422447a6d00f78a9f861bfc50de4ef5a628a57', class: "metrics-row" }, index.h("div", { key: 'c88a03fbff56f68796df33187256fd5a5a68f763', class: "metric-compact" }, index.h("span", { key: '5e366c35865f8bf45aecac126ce98e0e17be0825', class: "metric-label" }, "DET"), index.h("span", { key: '123570ac9ae59cf5ef1b992ea6c4563b753990c3', class: "metric-value good" }, this.performanceData.successfulDetections, "/", this.performanceData.totalDetections)), index.h("div", { key: 'dea3a496f84e80f9d6f27173c8fa2e4149bf8e0b', class: "metric-compact" }, index.h("span", { key: 'fe47d6c16ca84fff8824bef92d8ce9efe50dfa38', class: "metric-label" }, "RATE"), index.h("span", { key: '873846eb42c1ed97f4e9493871e51741766c852f', class: `metric-value ${this.performanceData.detectionRate < 30 ? 'danger' : this.performanceData.detectionRate < 60 ? 'warning' : 'good'}` }, this.performanceData.detectionRate, "%")))))), index.h("div", { key: 'b485b1d8fafee0314ffbf1c7934ffc9a3d4c6902', class: "watermark" }, index.h("img", { key: 'f5fae340c3e96ecafe26aa819dbc2411038aa953', src: "https://storage.googleapis.com/jaak-static/commons/powered-by-jaak.png", alt: "Powered by Jaak" })))));
|
|
1759
|
+
} })))), this.isMaskReady && (index.h("div", { key: '53066c626b23daae351ea9a80409e8882434b0dd', class: "overlay-mask" }, index.h("div", { key: '77bb4048d43e328e57a5c7e1909e0a59c8957217', class: "card-outline" }, index.h("div", { key: 'ea0f2610d903f985286856ab69f8be24fd42148e', class: "side side-top" }), index.h("div", { key: 'dc671111ef4adf33aa41f4457fdf344ede23eb80', class: "side side-right" }), index.h("div", { key: '2475ec41577dca92b425542c8861c570ed7ecd3b', class: "side side-bottom" }), index.h("div", { key: 'a06171c8eb1bd6353a6464485604560d73ac04b7', class: "side side-left" }), !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (index.h("div", { key: 'ecfd98f36d457fe7282b26f6a0efef5361380c76', class: "guide-text" }, "Alinee su identificaci\u00F3n con el marco"))), captureState.step === 'back' && !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (index.h("button", { key: 'f3c7b66500110437e9e4a7442cb327f2fdc6d71f', class: "skip-button", onClick: () => this.skipBackCapture(), type: "button" }, "Saltar reverso")), captureState.isVideoActive && (index.h("div", { key: 'b5b2a8dfc6dbcf381c32c4caa562c27ced2e3e56', class: "camera-controls" }, index.h("button", { key: '6b40d4649dfa22c5ea8ca90cacce35cd0ca53ff3', class: `camera-selector-button ${this.isSwitchingCamera ? 'loading' : ''}`, onClick: () => this.toggleCameraSelector(), type: "button", title: "Seleccionar c\u00E1mara", disabled: this.isSwitchingCamera }, this.isSwitchingCamera ? (index.h("div", { class: "button-spinner" })) : ('Cámaras')))), this.showCameraSelector && cameraInfo.availableCameras.length > 0 && (index.h("div", { key: '41feec90287510b08bd1090ad15586b323007c30', class: "camera-selector-dropdown" }, index.h("div", { key: '8310ebe3db842c463ad428b973f841053315a76f', class: "camera-selector-header" }, index.h("span", { key: '6693bdfa33643dfe73798d4f25b0c2d9de7aac7c' }, "Seleccionar C\u00E1mara"), index.h("button", { key: '8552d45e1482c570d7ff85588d4404eec7c369d7', class: "close-selector", onClick: () => this.toggleCameraSelector(), type: "button" }, "\u00D7")), index.h("div", { key: '5333ec0e5fb2c50a07a7c0b7d4dfb1bb4ee0df1c', class: "camera-list" }, cameraInfo.availableCameras.map((camera) => (index.h("button", { key: camera.id, class: `camera-option ${cameraInfo.selectedCameraId === camera.id ? 'selected' : ''}`, onClick: () => this.handleCameraSwitch(camera.id), type: "button" }, index.h("span", { class: "camera-label" }, camera.label || `Cámara ${cameraInfo.availableCameras.indexOf(camera) + 1}`), cameraInfo.selectedCameraId === camera.id && (index.h("span", { class: "selected-indicator" }, "\u2713")))))), index.h("div", { key: '8adf70b0e632c56a4784dec44621688809a3ff56', class: "device-info" }, index.h("small", { key: '2ea23922386ecda4a9011b50faba5efe4b4e60bb' }, "Dispositivo: ", cameraInfo.deviceType)))))), captureState.isCapturing && (index.h("div", { key: '8e693db67fe1e6ae8f11082b8993682b6d19b11d', class: "capture-animation" })), captureState.showFlipAnimation && (index.h("div", { key: '474489ed7848cdc163c492a5e19f5f13231e17b3', class: "flip-animation" }, index.h("div", { key: 'a9bd24ca21330882f001b7811077630eb1c30f50', class: "id-card-icon" }), index.h("div", { key: 'ffadb660a61e0f5e3e29bdc473d987e87dc4ad8a', class: "flip-text" }, "\u00A1Voltea tu identificaci\u00F3n!"))), captureState.showSuccessAnimation && (index.h("div", { key: 'd2522ee33aa159d49f040a293775892fb1cb81b5', class: "success-animation" }, index.h("div", { key: 'e7f899fac58bc9a3d51cf2cb89ee33bb320b241f', class: "check-icon" }), index.h("div", { key: 'd5d0ee1f37dbc659ca625ad03cbce325be0cf0a6', class: "success-text" }, "\u00A1Proceso completado!"))), index.h("div", { key: '425d347666032c3982a660ebee13b47670b996cb', class: `component-status status-${this.currentStatus.type}` }, (this.currentStatus.type === 'loading' || this.currentStatus.type === 'initializing') && (index.h("div", { key: '4d396fa107872f9a1e526fd59dc4edf279a05792', class: "status-spinner" })), index.h("div", { key: '232e9ae24c8cf5ef8ac0e3442e45a920799c724d', class: "status-content" }, index.h("div", { key: 'fe9dc0ca6d8e8276052bac7d2d44f1c7c9730364', class: "status-message" }, this.currentStatus.message), this.currentStatus.description && (index.h("div", { key: '6910c7ac78fd89e6fa1165446ec6d46fb356035c', class: "status-description" }, this.currentStatus.description)))), this.debug && (index.h("div", { key: '7282b8afaf03da53b01a23d04babd772528c3c14', class: "performance-monitor" }, index.h("div", { key: 'b582b3124fed13387cd70a02a34ab2bb541f3bfc', class: "performance-expanded" }, index.h("div", { key: '764bac2d61224b9a5f306f50934d410e1bd27787', class: "metrics-row" }, index.h("div", { key: '261563f8314a9f6069b1ac6362eb9c38936a9dc7', class: "metric-compact" }, index.h("span", { key: '4feea98eff86620c1bc90eb65d9799102423109d', class: "metric-label" }, "FPS"), index.h("span", { key: 'eb15f13b27b8aa50701ad3d11c4cf87200010b67', class: `metric-value ${this.performanceData.fps < 15 ? 'warning' : this.performanceData.fps < 10 ? 'danger' : 'good'}` }, this.performanceData.fps)), index.h("div", { key: '0813c35c2c33286f2276bfb5adce59b32c7bb597', class: "metric-compact" }, index.h("span", { key: '201e7bcb556e63b141e42a3ffc3419eb4284bfa8', class: "metric-label" }, "MEM"), index.h("span", { key: '4f6703cbe0128440d0f2f22f631035ab85422c64', class: `metric-value ${this.performanceData.memoryUsage > 100 ? 'warning' : this.performanceData.memoryUsage > 200 ? 'danger' : 'good'}` }, this.performanceData.memoryUsage, "MB"))), index.h("div", { key: '6343c361c93138b9ea66e8ae8acd361b93333f8c', class: "metrics-row" }, index.h("div", { key: 'd3c81fe597004bf380fae2d32f3a8314317e7769', class: "metric-compact" }, index.h("span", { key: 'fce572af3e200a611974dceef4da02f6aecedffc', class: "metric-label" }, "INF"), index.h("span", { key: '18dd78d54dcecfb70ddf59b782f87f8c6f208b0f', class: `metric-value ${this.performanceData.inferenceTime > 100 ? 'warning' : this.performanceData.inferenceTime > 200 ? 'danger' : 'good'}` }, this.performanceData.inferenceTime, "ms")), index.h("div", { key: '4c31ebfd835b017fd3e41b23d6bd9b19add511aa', class: "metric-compact" }, index.h("span", { key: '66b1a380b9479a8fc1e8c2385cb72aa4158dc74e', class: "metric-label" }, "FRAME"), index.h("span", { key: '3c6e41f7f11123c524d3a9df409345929d63f352', class: `metric-value ${this.performanceData.frameProcessingTime > 50 ? 'warning' : this.performanceData.frameProcessingTime > 100 ? 'danger' : 'good'}` }, this.performanceData.frameProcessingTime, "ms"))), index.h("div", { key: '3b522e2cdfe4bd9f1dd6eb0f91ee3577dc43e490', class: "metrics-row" }, index.h("div", { key: 'fa1e6f687b43aa2582b8b3426feb03a16c6493fc', class: "metric-compact" }, index.h("span", { key: 'a9355091e71a33a6a0d7cc6c11171dcda2e51b22', class: "metric-label" }, "DET"), index.h("span", { key: 'beca5a7ccf4853675cf8366bcf577ebfbd46c4f0', class: "metric-value good" }, this.performanceData.successfulDetections, "/", this.performanceData.totalDetections)), index.h("div", { key: '4b07fc9b3d35f99bc6e77c8726192cde0476a787', class: "metric-compact" }, index.h("span", { key: 'a1674b538f45328ee0afb00e582c07e0b4ccf553', class: "metric-label" }, "RATE"), index.h("span", { key: 'fd384a5137bc3df6c939d3d698a3ef80e9087030', class: `metric-value ${this.performanceData.detectionRate < 30 ? 'danger' : this.performanceData.detectionRate < 60 ? 'warning' : 'good'}` }, this.performanceData.detectionRate, "%")))))), index.h("div", { key: 'ebbaf15a0ad96e4da5bbf746c32d7a4abb51b973', class: "watermark" }, index.h("img", { key: '8e7d274b529d1f7020ec4a7225579792763f871a', src: "https://storage.googleapis.com/jaak-static/commons/powered-by-jaak.png", alt: "Powered by Jaak" })))));
|
|
2381
1760
|
}
|
|
2382
1761
|
// Utility methods
|
|
2383
1762
|
updateDetectionBoxes(boxes) {
|
|
@@ -2449,19 +1828,14 @@ const JaakStamps = class {
|
|
|
2449
1828
|
if (allSidesAligned && bestBox) {
|
|
2450
1829
|
cardOutline?.classList.add('perfect-match');
|
|
2451
1830
|
corners?.forEach(corner => corner.classList.add('perfect-match'));
|
|
2452
|
-
// Check quality validation before allowing capture
|
|
2453
|
-
const qualityCheckPassed = !this.enableQualityValidation || this.qualityFeedback.canCapture;
|
|
2454
1831
|
// Debug logging
|
|
2455
1832
|
this.logger.state('CAPTURE_EVALUATION', {
|
|
2456
1833
|
allSidesAligned: true,
|
|
2457
1834
|
hasScreenshotTaken: this.hasScreenshotTaken,
|
|
2458
|
-
qualityCheckPassed,
|
|
2459
|
-
enableQualityValidation: this.enableQualityValidation,
|
|
2460
|
-
qualityFeedback: this.qualityFeedback,
|
|
2461
1835
|
captureDelay: this.captureDelay,
|
|
2462
1836
|
alignmentStartTime: this.alignmentStartTime
|
|
2463
1837
|
});
|
|
2464
|
-
if (!this.hasScreenshotTaken
|
|
1838
|
+
if (!this.hasScreenshotTaken) {
|
|
2465
1839
|
const currentTime = Date.now();
|
|
2466
1840
|
// Initialize alignment start time if not set
|
|
2467
1841
|
if (!this.alignmentStartTime) {
|
|
@@ -2478,8 +1852,7 @@ const JaakStamps = class {
|
|
|
2478
1852
|
if (alignmentDuration >= this.captureDelay) {
|
|
2479
1853
|
this.logger.state('TRIGGERING_CAPTURE', {
|
|
2480
1854
|
alignmentDuration,
|
|
2481
|
-
captureDelay: this.captureDelay
|
|
2482
|
-
qualityScore: this.qualityFeedback.score
|
|
1855
|
+
captureDelay: this.captureDelay
|
|
2483
1856
|
});
|
|
2484
1857
|
this.lastDetectedBox = bestBox;
|
|
2485
1858
|
this.takeScreenshot().catch(error => {
|
|
@@ -2492,17 +1865,6 @@ const JaakStamps = class {
|
|
|
2492
1865
|
}, 2000);
|
|
2493
1866
|
}
|
|
2494
1867
|
}
|
|
2495
|
-
else if (!qualityCheckPassed) {
|
|
2496
|
-
// Reset alignment timer if quality check fails
|
|
2497
|
-
this.logger.state('QUALITY_CHECK_FAILED', {
|
|
2498
|
-
enableQualityValidation: this.enableQualityValidation,
|
|
2499
|
-
canCapture: this.qualityFeedback.canCapture,
|
|
2500
|
-
qualityScore: this.qualityFeedback.score,
|
|
2501
|
-
hasIssues: this.qualityFeedback.hasIssues
|
|
2502
|
-
});
|
|
2503
|
-
this.alignmentStartTime = undefined;
|
|
2504
|
-
cardOutline?.classList.add('quality-warning');
|
|
2505
|
-
}
|
|
2506
1868
|
}
|
|
2507
1869
|
else {
|
|
2508
1870
|
cardOutline?.classList.remove('perfect-match');
|