tien_nemo

test load data

......@@ -27,8 +27,8 @@
.bbox.focus-highlight {
animation: focusPulse 2s ease-in-out;
border-color: #ff6b35 !important;
background-color: rgba(255, 107, 53, 0.4) !important;
/*border-color: #ff6b35 !important;*/
/*background-color: rgba(255, 107, 53, 0.4) !important;*/
}
@keyframes focusPulse {
......@@ -137,6 +137,8 @@
>
</div>
<button @click="saveTemplate">💾Save</button>
{{-- <button @click="debugCoordinates" style="margin-left: 10px; background: #6c757d;">🐛Debug</button>--}}
{{-- <button @click="testManualBox" style="margin-left: 10px; background: #28a745;">🧪Test Box</button>--}}
</div>
</div>
......@@ -184,7 +186,7 @@
mapFieldToBox(index, fieldName, text = null) {
if (index == null) return;
// Xóa fieldName ở box khác
// Xóa fieldName ở box khác đảm bảo mỗi field chỉ gán cho 1 box duy nhất
this.ocrData.forEach((box, i) => {
if (i !== index && box.field === fieldName) {
box.field = null;
......@@ -192,7 +194,7 @@
}
});
// Nếu box này từng gán field khác thì bỏ
// Nếu box này từng gán field khác thì bỏ reset flag và tọa độ liên quan.
const prev = this.ocrData[index].field;
if (prev && prev !== fieldName) {
if (prev === 'customer_name') {
......@@ -206,7 +208,6 @@
// Gán field mới
const bbox = this.ocrData[index].bbox; // tọa độ OCR gốc [x1, y1, x2, y2]
console.log('1111111111111111',bbox);
const x1 = bbox[0];
const y1 = bbox[1];
const w = bbox[2];
......@@ -231,6 +232,7 @@
// Assign field và set active (dùng khi user tương tác)
assignFieldToBox(index, fieldName, text = null) {
console.log(`Assigning field "${fieldName}" to box at index ${index} with text: "${text}"`);
if (index == null) return;
// Xóa fieldName ở box khác
......@@ -255,7 +257,7 @@
// Gán field mới
const bbox = this.ocrData[index].bbox; // tọa độ OCR gốc [x1, y1, x2, y2]
console.log('1111111111111111',bbox);
console.log('2222222222222222',bbox);
const x1 = bbox[0];
const y1 = bbox[1];
const w = bbox[2];
......@@ -307,7 +309,7 @@
customer_name_xy: this.customer_name_xy,
fields: fields
};
console.log(fields);
// console.log(fields);
try {
const res = await fetch('/ocr/save-template', {
method: 'POST',
......@@ -360,21 +362,44 @@
async loadOCRData() {
const res = await fetch(`/ocr/data-list`);
const data = await res.json();
try {
const res = await fetch(`/ocr/data-list`);
const data = await res.json();
if (data.error) {
console.error(data.error);
return;
}
if (data.error) {
console.error('Error loading data:', data.error);
return;
}
this.ocrData = data.ocrData;
this.pdfImageUrl = data.pdfImageUrl;
this.formData = data.formData;
this.fieldOptions = data.fieldOptions;
this.ocrData = data.ocrData;
this.pdfImageUrl = data.pdfImageUrl;
this.formData = data.formData;
this.fieldOptions = data.fieldOptions;
// Đợi image load xong trước khi xử lý
if (this.$refs.pdfImage && this.$refs.pdfImage.complete) {
this.processLoadedData();
} else {
console.log('Image not loaded yet, waiting for onImageLoad');
// Image sẽ được xử lý trong onImageLoad
}
} catch (error) {
console.error('Error in loadOCRData:', error);
}
},
// Xử lý data sau khi image đã load
processLoadedData() {
// Tự động map field cho các box OCR dựa trên formData đã load
this.autoMapFieldsFromFormData();
// Kiểm tra và sửa lại tọa độ của các box manual
// this.validateManualBoxes();
// Force re-render để đảm bảo các box được vẽ
this.$nextTick(() => {
this.$forceUpdate();
});
},
// Tự động map field cho các box OCR dựa trên formData đã load từ DB
......@@ -394,8 +419,10 @@
}
}
});
},
// Tìm box OCR phù hợp nhất để map với field
findBestMatchingBox(fieldName, fieldValue) {
let bestMatchIndex = -1;
......@@ -427,7 +454,6 @@
const t1 = text1.toLowerCase().trim();
const t2 = text2.toLowerCase().trim();
// Nếu text giống hệt nhau
if (t1 === t2) return 1.0;
......@@ -448,9 +474,18 @@
const img = this.$refs.pdfImage;
this.imageWidth = img.naturalWidth;
this.imageHeight = img.naturalHeight;
// Nếu đã có data, xử lý ngay
if (this.ocrData && this.ocrData.length > 0) {
console.log('Image loaded and data exists, processing now');
this.processLoadedData();
} else {
console.log('Image loaded but no data yet');
}
},
getBoxStyle(item, index) {
if (!this.imageWidth || !this.imageHeight || !this.$refs.pdfImage) return {};
if (!this.imageWidth || !this.imageHeight || !this.$refs.pdfImage) {
return {};
}
const [x1, y1, x2, y2] = item.bbox;
const displayedWidth = this.$refs.pdfImage.clientWidth;
......@@ -459,14 +494,18 @@
const scaleX = displayedWidth / this.imageWidth;
const scaleY = displayedHeight / this.imageHeight;
const left = Math.round(x1 * scaleX);
const top = Math.round(y1 * scaleY);
const width = Math.round((x2 - x1) * scaleX);
const height = Math.round((y2 - y1) * scaleY);
return {
position: 'absolute',
left: `${Math.round(x1 * scaleX)}px`,
top: `${Math.round(y1 * scaleY)}px`,
width: `${Math.round((x2 - x1) * scaleX)}px`,
height: `${Math.round((y2 - y1) * scaleY)}px`,
left: `${left}px`,
top: `${top}px`,
width: `${width}px`,
height: `${height}px`,
border: item.hideBorder ? 'none' : '2px solid ' + (index === this.activeIndex ? '#199601' : '#ff5252'),
//backgroundColor: item.hideBorder ? 'transparent' : (this.activeIndex === item.field ? 'rgba(33,150,243,0.3)' : 'rgba(255,82,82,0.2)'),
boxSizing: 'border-box',
cursor: 'pointer',
zIndex: item.isManual ? 30 : 10
......@@ -475,6 +514,7 @@
highlightField(field) {
let idx = -1;
console.log(`Highlighting field: ${field}`);
for (let i = this.ocrData.length - 1; i >= 0; i--) {
const it = this.ocrData[i];
if (!it.isDeleted && it.field === field) {
......@@ -482,6 +522,7 @@
break;
}
}
console.log('ssss', idx, this.ocrData[idx]);
if (idx !== -1) {
// Set active index (chuyển trạng thái active và màu xanh)
......@@ -500,10 +541,13 @@
if (!this.$refs.pdfContainer || index < 0 || index >= this.ocrData.length) return;
const item = this.ocrData[index];
console.log(`Scrolling to box at index ${index}:`, item);
if (!item || item.isDeleted) return;
// Tính vị trí hiển thị của box
const [x1, y1, x2, y2] = item.bbox;
// const [x1, y1, x2, y2] = item.bbox;
const [x1, y1, x2, y2] = item.field_xy.split(',').map(Number);
console.log(`Box coordinates for scrolling: [${x1}, ${y1}, ${x2}, ${y2}]`);
if (!this.imageWidth || !this.imageHeight || !this.$refs.pdfImage) return;
const displayedWidth = this.$refs.pdfImage.clientWidth;
......@@ -601,7 +645,7 @@
const dispX2 = this.selectBox.x + this.selectBox.width;
const dispY2 = this.selectBox.y + this.selectBox.height;
// scale: displayed -> original
// scale: displayed -> original (sửa lại để chính xác hơn)
const displayedWidth = this.$refs.pdfImage.clientWidth;
const displayedHeight = this.$refs.pdfImage.clientHeight;
const scaleX = this.imageWidth / displayedWidth;
......@@ -661,39 +705,76 @@
applyManualMapping() {
if (!this.manualField) return;
const manualIndex = this.manualIndex;
const newBbox = this.ocrData[manualIndex].bbox;
console.log(`33333 ${manualIndex} with field "${this.manualField}" new box ${newBbox}`);
// console.log('Applying manual mapping for field:', this.manualField);
// console.log('Manual bbox:', newBbox);
let combinedText = [];
let foundItems = [];
// Tìm tất cả các box OCR nằm trong vùng manual
this.ocrData.forEach(item => {
if (!item.isManual && this.isBoxInside(item.bbox, newBbox) && item.text.trim()) {
const partial = this.getPartialText(item.text, item.bbox, newBbox);
if (partial) combinedText.push(partial);
// combinedText.push(item.text.trim());
foundItems.push({
text: item.text,
bbox: item.bbox,
index: this.ocrData.indexOf(item)
});
}
});
// console.log('Found OCR items in manual area:', foundItems);
// Sắp xếp các item theo vị trí (từ trái sang phải, từ trên xuống dưới)
foundItems.sort((a, b) => {
// Ưu tiên theo Y trước (hàng), sau đó theo X (cột)
if (Math.abs(a.bbox[1] - b.bbox[1]) < 20) { // Cùng hàng (tolerance 20px)
return a.bbox[0] - b.bbox[0]; // Sắp xếp theo X
}
return a.bbox[1] - b.bbox[1]; // Sắp xếp theo Y
});
// Gộp text theo thứ tự đã sắp xếp
foundItems.forEach(item => {
combinedText.push(item.text.trim());
});
const finalText = combinedText.join(" ");
// this.ocrData[manualIndex].field = this.manualField;
// this.formData[this.manualField] = finalText;
// this.activeIndex = manualIndex;
// console.log('Combined text:', finalText);
// Gán field và text cho box manual
this.assignFieldToBox(manualIndex, this.manualField, finalText);
// Reset trạng thái chọn
this.isMappingManually = false;
this.selectBox.show = false;
this.selectBox.showDropdown = false;
// this.manualField = "";
// this.manualIndex = null;
},
isBoxInside(inner, outer) {
return !(
// inner: bbox của OCR item [x1, y1, x2, y2]
// outer: bbox của vùng manual [x1, y1, x2, y2]
// Kiểm tra xem box OCR có nằm hoàn toàn trong vùng manual không
const isFullyInside = (
inner[0] >= outer[0] && // left edge
inner[1] >= outer[1] && // top edge
inner[2] <= outer[2] && // right edge
inner[3] <= outer[3] // bottom edge
);
// Kiểm tra xem box OCR có giao nhau với vùng manual không
const isOverlapping = !(
inner[2] < outer[0] || // box bên trái vùng chọn
inner[0] > outer[2] || // box bên phải vùng chọn
inner[3] < outer[1] || // box phía trên vùng chọn
inner[1] > outer[3] // box phía dưới vùng chọn
);
// Trả về true nếu box OCR nằm hoàn toàn trong hoặc giao nhau đáng kể
return isFullyInside || isOverlapping;
},
......@@ -704,6 +785,7 @@
// Chiều rộng box OCR
const boxWidth = x2 - x1;
const boxHeight = y2 - y1;
// Vị trí start và end tương đối trong text
let startRatio = Math.max(0, (sx1 - x1) / boxWidth);
......@@ -712,7 +794,18 @@
const startIndex = Math.floor(startRatio * text.length);
const endIndex = Math.ceil(endRatio * text.length);
return text.substring(startIndex, endIndex).trim();
const partialText = text.substring(startIndex, endIndex).trim();
// console.log('Partial text calculation:', {
// originalText: text,
// bbox: bbox,
// selectBbox: selectBbox,
// startRatio, endRatio,
// startIndex, endIndex,
// partialText
// });
return partialText;
},
getSelectStyle(item) {
if (!this.imageWidth) return { position: 'absolute' };
......@@ -729,6 +822,185 @@
top: `${Math.round(y2 * scaleY)}px`,
zIndex: 9999
};
},
// Debug method để kiểm tra tọa độ
debugCoordinates() {
console.log('=== DEBUG COORDINATES ===');
console.log('Image dimensions:', {
natural: { width: this.imageWidth, height: this.imageHeight },
displayed: {
width: this.$refs.pdfImage?.clientWidth,
height: this.$refs.pdfImage?.clientHeight
}
});
console.log('Scale factors:', {
scaleX: this.$refs.pdfImage ? this.$refs.pdfImage.clientWidth / this.imageWidth : 'N/A',
scaleY: this.$refs.pdfImage ? this.$refs.pdfImage.clientHeight / this.imageHeight : 'N/A'
});
console.log('OCR Data with coordinates:');
this.ocrData.forEach((item, index) => {
if (!item.isDeleted) {
console.log(`Item ${index}:`, {
text: item.text,
bbox: item.bbox,
field: item.field,
isManual: item.isManual
});
}
});
console.log('=== END DEBUG ===');
},
// Kiểm tra và sửa lại tọa độ của các box manual
validateManualBoxes() {
if (!this.imageWidth || !this.imageHeight) {
console.log('validateManualBoxes: Image not loaded yet');
return;
}
this.ocrData.forEach((item, index) => {
if (item.isManual && !item.isDeleted) {
const [x1, y1, x2, y2] = item.bbox;
const isValid = (
x1 >= 0 && y1 >= 0 &&
x2 > x1 && y2 > y1 &&
x2 <= this.imageWidth && y2 <= this.imageHeight
);
if (!isValid) {
// Thử sửa lại tọa độ nếu có thể
this.fixManualBoxCoordinates(item, index);
}
}
});
console.log('=== END VALIDATION ===');
// Force re-render để đảm bảo các box được vẽ đúng
this.$nextTick(() => {
this.$forceUpdate();
});
},
// Sửa lại tọa độ của box manual nếu bị lỗi
fixManualBoxCoordinates(item, index) {
console.log(`Attempting to fix manual box ${index}:`, item);
// Nếu tọa độ âm, đặt về 0
let [x1, y1, x2, y2] = item.bbox;
if (x1 < 0) x1 = 0;
if (y1 < 0) y1 = 0;
if (x2 <= x1) x2 = x1 + 100; // Tạo width mặc định
if (y2 <= y1) y2 = y1 + 50; // Tạo height mặc định
// Đảm bảo không vượt quá image bounds
if (x2 > this.imageWidth) x2 = this.imageWidth;
if (y2 > this.imageHeight) y2 = this.imageHeight;
const fixedBbox = [x1, y1, x2, y2];
console.log(`Fixed bbox for manual box ${index}:`, {
original: item.bbox,
fixed: fixedBbox
});
// Cập nhật tọa độ
this.$set(this.ocrData[index], 'bbox', fixedBbox);
},
// Test method để tạo box manual test
testManualBox() {
if (!this.imageWidth || !this.imageHeight) {
alert('Image chưa được load. Vui lòng đợi image load xong.');
return;
}
// Tạo một box manual test ở giữa image
const centerX = Math.round(this.imageWidth / 2);
const centerY = Math.round(this.imageHeight / 2);
const boxSize = 100;
const testBbox = [
centerX - boxSize/2, // x1
centerY - boxSize/2, // y1
centerX + boxSize/2, // x2
centerY + boxSize/2 // y2
];
console.log('Creating test manual box:', {
bbox: testBbox,
imageDimensions: { width: this.imageWidth, height: this.imageHeight }
});
// Thêm box test
this.ocrData.push({
text: "TEST BOX",
bbox: testBbox,
field: "test_field",
isManual: true,
showDelete: true,
isDeleted: false,
hideBorder: false
});
// Force re-render
this.$forceUpdate();
},
// Tạo box manual từ tọa độ trong DB
createManualBoxFromDB(fieldName, coordinates, text) {
if (!this.imageWidth || !this.imageHeight) {
console.log('Cannot create manual box: Image not loaded');
return;
}
// Parse coordinates từ string "x1,y1,x2,y2"
let coords;
if (typeof coordinates === 'string') {
coords = coordinates.split(',').map(Number);
} else if (Array.isArray(coordinates)) {
coords = coordinates;
} else {
console.error('Invalid coordinates format:', coordinates);
return;
}
const [x1, y1, x2, y2] = coords;
console.log('Creating manual box from DB:', {
fieldName,
coordinates,
parsed: coords,
imageDimensions: { width: this.imageWidth, height: this.imageHeight }
});
// Kiểm tra tọa độ có hợp lệ không
if (x1 >= 0 && y1 >= 0 && x2 > x1 && y2 > y1 &&
x2 <= this.imageWidth && y2 <= this.imageHeight) {
// Tạo box manual
const manualBox = {
text: text || '',
bbox: coords,
field: fieldName,
isManual: true,
showDelete: true,
isDeleted: false,
hideBorder: false
};
this.ocrData.push(manualBox);
console.log('Manual box created successfully:', manualBox);
// Force re-render
this.$forceUpdate();
} else {
console.warn('Invalid coordinates for manual box:', coords);
}
}
}
......