Showing
1 changed file
with
299 additions
and
27 deletions
| ... | @@ -27,8 +27,8 @@ | ... | @@ -27,8 +27,8 @@ |
| 27 | 27 | ||
| 28 | .bbox.focus-highlight { | 28 | .bbox.focus-highlight { |
| 29 | animation: focusPulse 2s ease-in-out; | 29 | animation: focusPulse 2s ease-in-out; |
| 30 | - border-color: #ff6b35 !important; | 30 | + /*border-color: #ff6b35 !important;*/ |
| 31 | - background-color: rgba(255, 107, 53, 0.4) !important; | 31 | + /*background-color: rgba(255, 107, 53, 0.4) !important;*/ |
| 32 | } | 32 | } |
| 33 | 33 | ||
| 34 | @keyframes focusPulse { | 34 | @keyframes focusPulse { |
| ... | @@ -137,6 +137,8 @@ | ... | @@ -137,6 +137,8 @@ |
| 137 | > | 137 | > |
| 138 | </div> | 138 | </div> |
| 139 | <button @click="saveTemplate">💾Save</button> | 139 | <button @click="saveTemplate">💾Save</button> |
| 140 | +{{-- <button @click="debugCoordinates" style="margin-left: 10px; background: #6c757d;">🐛Debug</button>--}} | ||
| 141 | +{{-- <button @click="testManualBox" style="margin-left: 10px; background: #28a745;">🧪Test Box</button>--}} | ||
| 140 | </div> | 142 | </div> |
| 141 | 143 | ||
| 142 | </div> | 144 | </div> |
| ... | @@ -184,7 +186,7 @@ | ... | @@ -184,7 +186,7 @@ |
| 184 | mapFieldToBox(index, fieldName, text = null) { | 186 | mapFieldToBox(index, fieldName, text = null) { |
| 185 | if (index == null) return; | 187 | if (index == null) return; |
| 186 | 188 | ||
| 187 | - // Xóa fieldName ở box khác | 189 | + // Xóa fieldName ở box khác đảm bảo mỗi field chỉ gán cho 1 box duy nhất |
| 188 | this.ocrData.forEach((box, i) => { | 190 | this.ocrData.forEach((box, i) => { |
| 189 | if (i !== index && box.field === fieldName) { | 191 | if (i !== index && box.field === fieldName) { |
| 190 | box.field = null; | 192 | box.field = null; |
| ... | @@ -192,7 +194,7 @@ | ... | @@ -192,7 +194,7 @@ |
| 192 | } | 194 | } |
| 193 | }); | 195 | }); |
| 194 | 196 | ||
| 195 | - // Nếu box này từng gán field khác thì bỏ | 197 | + // Nếu box này từng gán field khác thì bỏ reset flag và tọa độ liên quan. |
| 196 | const prev = this.ocrData[index].field; | 198 | const prev = this.ocrData[index].field; |
| 197 | if (prev && prev !== fieldName) { | 199 | if (prev && prev !== fieldName) { |
| 198 | if (prev === 'customer_name') { | 200 | if (prev === 'customer_name') { |
| ... | @@ -206,7 +208,6 @@ | ... | @@ -206,7 +208,6 @@ |
| 206 | // Gán field mới | 208 | // Gán field mới |
| 207 | const bbox = this.ocrData[index].bbox; // tọa độ OCR gốc [x1, y1, x2, y2] | 209 | const bbox = this.ocrData[index].bbox; // tọa độ OCR gốc [x1, y1, x2, y2] |
| 208 | 210 | ||
| 209 | - console.log('1111111111111111',bbox); | ||
| 210 | const x1 = bbox[0]; | 211 | const x1 = bbox[0]; |
| 211 | const y1 = bbox[1]; | 212 | const y1 = bbox[1]; |
| 212 | const w = bbox[2]; | 213 | const w = bbox[2]; |
| ... | @@ -231,6 +232,7 @@ | ... | @@ -231,6 +232,7 @@ |
| 231 | 232 | ||
| 232 | // Assign field và set active (dùng khi user tương tác) | 233 | // Assign field và set active (dùng khi user tương tác) |
| 233 | assignFieldToBox(index, fieldName, text = null) { | 234 | assignFieldToBox(index, fieldName, text = null) { |
| 235 | + console.log(`Assigning field "${fieldName}" to box at index ${index} with text: "${text}"`); | ||
| 234 | if (index == null) return; | 236 | if (index == null) return; |
| 235 | 237 | ||
| 236 | // Xóa fieldName ở box khác | 238 | // Xóa fieldName ở box khác |
| ... | @@ -255,7 +257,7 @@ | ... | @@ -255,7 +257,7 @@ |
| 255 | // Gán field mới | 257 | // Gán field mới |
| 256 | const bbox = this.ocrData[index].bbox; // tọa độ OCR gốc [x1, y1, x2, y2] | 258 | const bbox = this.ocrData[index].bbox; // tọa độ OCR gốc [x1, y1, x2, y2] |
| 257 | 259 | ||
| 258 | - console.log('1111111111111111',bbox); | 260 | + console.log('2222222222222222',bbox); |
| 259 | const x1 = bbox[0]; | 261 | const x1 = bbox[0]; |
| 260 | const y1 = bbox[1]; | 262 | const y1 = bbox[1]; |
| 261 | const w = bbox[2]; | 263 | const w = bbox[2]; |
| ... | @@ -307,7 +309,7 @@ | ... | @@ -307,7 +309,7 @@ |
| 307 | customer_name_xy: this.customer_name_xy, | 309 | customer_name_xy: this.customer_name_xy, |
| 308 | fields: fields | 310 | fields: fields |
| 309 | }; | 311 | }; |
| 310 | - console.log(fields); | 312 | + // console.log(fields); |
| 311 | try { | 313 | try { |
| 312 | const res = await fetch('/ocr/save-template', { | 314 | const res = await fetch('/ocr/save-template', { |
| 313 | method: 'POST', | 315 | method: 'POST', |
| ... | @@ -360,11 +362,12 @@ | ... | @@ -360,11 +362,12 @@ |
| 360 | 362 | ||
| 361 | async loadOCRData() { | 363 | async loadOCRData() { |
| 362 | 364 | ||
| 365 | + try { | ||
| 363 | const res = await fetch(`/ocr/data-list`); | 366 | const res = await fetch(`/ocr/data-list`); |
| 364 | const data = await res.json(); | 367 | const data = await res.json(); |
| 365 | 368 | ||
| 366 | if (data.error) { | 369 | if (data.error) { |
| 367 | - console.error(data.error); | 370 | + console.error('Error loading data:', data.error); |
| 368 | return; | 371 | return; |
| 369 | } | 372 | } |
| 370 | 373 | ||
| ... | @@ -372,9 +375,31 @@ | ... | @@ -372,9 +375,31 @@ |
| 372 | this.pdfImageUrl = data.pdfImageUrl; | 375 | this.pdfImageUrl = data.pdfImageUrl; |
| 373 | this.formData = data.formData; | 376 | this.formData = data.formData; |
| 374 | this.fieldOptions = data.fieldOptions; | 377 | this.fieldOptions = data.fieldOptions; |
| 378 | + // Đợi image load xong trước khi xử lý | ||
| 379 | + if (this.$refs.pdfImage && this.$refs.pdfImage.complete) { | ||
| 380 | + this.processLoadedData(); | ||
| 381 | + } else { | ||
| 382 | + console.log('Image not loaded yet, waiting for onImageLoad'); | ||
| 383 | + // Image sẽ được xử lý trong onImageLoad | ||
| 384 | + } | ||
| 385 | + | ||
| 386 | + | ||
| 387 | + } catch (error) { | ||
| 388 | + console.error('Error in loadOCRData:', error); | ||
| 389 | + } | ||
| 390 | + }, | ||
| 375 | 391 | ||
| 392 | + // Xử lý data sau khi image đã load | ||
| 393 | + processLoadedData() { | ||
| 376 | // Tự động map field cho các box OCR dựa trên formData đã load | 394 | // Tự động map field cho các box OCR dựa trên formData đã load |
| 377 | this.autoMapFieldsFromFormData(); | 395 | this.autoMapFieldsFromFormData(); |
| 396 | + // Kiểm tra và sửa lại tọa độ của các box manual | ||
| 397 | + // this.validateManualBoxes(); | ||
| 398 | + | ||
| 399 | + // Force re-render để đảm bảo các box được vẽ | ||
| 400 | + this.$nextTick(() => { | ||
| 401 | + this.$forceUpdate(); | ||
| 402 | + }); | ||
| 378 | }, | 403 | }, |
| 379 | 404 | ||
| 380 | // Tự động map field cho các box OCR dựa trên formData đã load từ DB | 405 | // Tự động map field cho các box OCR dựa trên formData đã load từ DB |
| ... | @@ -394,8 +419,10 @@ | ... | @@ -394,8 +419,10 @@ |
| 394 | } | 419 | } |
| 395 | } | 420 | } |
| 396 | }); | 421 | }); |
| 422 | + | ||
| 397 | }, | 423 | }, |
| 398 | 424 | ||
| 425 | + | ||
| 399 | // Tìm box OCR phù hợp nhất để map với field | 426 | // Tìm box OCR phù hợp nhất để map với field |
| 400 | findBestMatchingBox(fieldName, fieldValue) { | 427 | findBestMatchingBox(fieldName, fieldValue) { |
| 401 | let bestMatchIndex = -1; | 428 | let bestMatchIndex = -1; |
| ... | @@ -427,7 +454,6 @@ | ... | @@ -427,7 +454,6 @@ |
| 427 | 454 | ||
| 428 | const t1 = text1.toLowerCase().trim(); | 455 | const t1 = text1.toLowerCase().trim(); |
| 429 | const t2 = text2.toLowerCase().trim(); | 456 | const t2 = text2.toLowerCase().trim(); |
| 430 | - | ||
| 431 | // Nếu text giống hệt nhau | 457 | // Nếu text giống hệt nhau |
| 432 | if (t1 === t2) return 1.0; | 458 | if (t1 === t2) return 1.0; |
| 433 | 459 | ||
| ... | @@ -448,9 +474,18 @@ | ... | @@ -448,9 +474,18 @@ |
| 448 | const img = this.$refs.pdfImage; | 474 | const img = this.$refs.pdfImage; |
| 449 | this.imageWidth = img.naturalWidth; | 475 | this.imageWidth = img.naturalWidth; |
| 450 | this.imageHeight = img.naturalHeight; | 476 | this.imageHeight = img.naturalHeight; |
| 477 | + // Nếu đã có data, xử lý ngay | ||
| 478 | + if (this.ocrData && this.ocrData.length > 0) { | ||
| 479 | + console.log('Image loaded and data exists, processing now'); | ||
| 480 | + this.processLoadedData(); | ||
| 481 | + } else { | ||
| 482 | + console.log('Image loaded but no data yet'); | ||
| 483 | + } | ||
| 451 | }, | 484 | }, |
| 452 | getBoxStyle(item, index) { | 485 | getBoxStyle(item, index) { |
| 453 | - if (!this.imageWidth || !this.imageHeight || !this.$refs.pdfImage) return {}; | 486 | + if (!this.imageWidth || !this.imageHeight || !this.$refs.pdfImage) { |
| 487 | + return {}; | ||
| 488 | + } | ||
| 454 | 489 | ||
| 455 | const [x1, y1, x2, y2] = item.bbox; | 490 | const [x1, y1, x2, y2] = item.bbox; |
| 456 | const displayedWidth = this.$refs.pdfImage.clientWidth; | 491 | const displayedWidth = this.$refs.pdfImage.clientWidth; |
| ... | @@ -459,14 +494,18 @@ | ... | @@ -459,14 +494,18 @@ |
| 459 | const scaleX = displayedWidth / this.imageWidth; | 494 | const scaleX = displayedWidth / this.imageWidth; |
| 460 | const scaleY = displayedHeight / this.imageHeight; | 495 | const scaleY = displayedHeight / this.imageHeight; |
| 461 | 496 | ||
| 497 | + const left = Math.round(x1 * scaleX); | ||
| 498 | + const top = Math.round(y1 * scaleY); | ||
| 499 | + const width = Math.round((x2 - x1) * scaleX); | ||
| 500 | + const height = Math.round((y2 - y1) * scaleY); | ||
| 501 | + | ||
| 462 | return { | 502 | return { |
| 463 | position: 'absolute', | 503 | position: 'absolute', |
| 464 | - left: `${Math.round(x1 * scaleX)}px`, | 504 | + left: `${left}px`, |
| 465 | - top: `${Math.round(y1 * scaleY)}px`, | 505 | + top: `${top}px`, |
| 466 | - width: `${Math.round((x2 - x1) * scaleX)}px`, | 506 | + width: `${width}px`, |
| 467 | - height: `${Math.round((y2 - y1) * scaleY)}px`, | 507 | + height: `${height}px`, |
| 468 | border: item.hideBorder ? 'none' : '2px solid ' + (index === this.activeIndex ? '#199601' : '#ff5252'), | 508 | border: item.hideBorder ? 'none' : '2px solid ' + (index === this.activeIndex ? '#199601' : '#ff5252'), |
| 469 | - //backgroundColor: item.hideBorder ? 'transparent' : (this.activeIndex === item.field ? 'rgba(33,150,243,0.3)' : 'rgba(255,82,82,0.2)'), | ||
| 470 | boxSizing: 'border-box', | 509 | boxSizing: 'border-box', |
| 471 | cursor: 'pointer', | 510 | cursor: 'pointer', |
| 472 | zIndex: item.isManual ? 30 : 10 | 511 | zIndex: item.isManual ? 30 : 10 |
| ... | @@ -475,6 +514,7 @@ | ... | @@ -475,6 +514,7 @@ |
| 475 | 514 | ||
| 476 | highlightField(field) { | 515 | highlightField(field) { |
| 477 | let idx = -1; | 516 | let idx = -1; |
| 517 | + console.log(`Highlighting field: ${field}`); | ||
| 478 | for (let i = this.ocrData.length - 1; i >= 0; i--) { | 518 | for (let i = this.ocrData.length - 1; i >= 0; i--) { |
| 479 | const it = this.ocrData[i]; | 519 | const it = this.ocrData[i]; |
| 480 | if (!it.isDeleted && it.field === field) { | 520 | if (!it.isDeleted && it.field === field) { |
| ... | @@ -482,6 +522,7 @@ | ... | @@ -482,6 +522,7 @@ |
| 482 | break; | 522 | break; |
| 483 | } | 523 | } |
| 484 | } | 524 | } |
| 525 | + console.log('ssss', idx, this.ocrData[idx]); | ||
| 485 | 526 | ||
| 486 | if (idx !== -1) { | 527 | if (idx !== -1) { |
| 487 | // Set active index (chuyển trạng thái active và màu xanh) | 528 | // Set active index (chuyển trạng thái active và màu xanh) |
| ... | @@ -500,10 +541,13 @@ | ... | @@ -500,10 +541,13 @@ |
| 500 | if (!this.$refs.pdfContainer || index < 0 || index >= this.ocrData.length) return; | 541 | if (!this.$refs.pdfContainer || index < 0 || index >= this.ocrData.length) return; |
| 501 | 542 | ||
| 502 | const item = this.ocrData[index]; | 543 | const item = this.ocrData[index]; |
| 544 | + console.log(`Scrolling to box at index ${index}:`, item); | ||
| 503 | if (!item || item.isDeleted) return; | 545 | if (!item || item.isDeleted) return; |
| 504 | 546 | ||
| 505 | // Tính vị trí hiển thị của box | 547 | // Tính vị trí hiển thị của box |
| 506 | - const [x1, y1, x2, y2] = item.bbox; | 548 | + // const [x1, y1, x2, y2] = item.bbox; |
| 549 | + const [x1, y1, x2, y2] = item.field_xy.split(',').map(Number); | ||
| 550 | + console.log(`Box coordinates for scrolling: [${x1}, ${y1}, ${x2}, ${y2}]`); | ||
| 507 | if (!this.imageWidth || !this.imageHeight || !this.$refs.pdfImage) return; | 551 | if (!this.imageWidth || !this.imageHeight || !this.$refs.pdfImage) return; |
| 508 | 552 | ||
| 509 | const displayedWidth = this.$refs.pdfImage.clientWidth; | 553 | const displayedWidth = this.$refs.pdfImage.clientWidth; |
| ... | @@ -601,7 +645,7 @@ | ... | @@ -601,7 +645,7 @@ |
| 601 | const dispX2 = this.selectBox.x + this.selectBox.width; | 645 | const dispX2 = this.selectBox.x + this.selectBox.width; |
| 602 | const dispY2 = this.selectBox.y + this.selectBox.height; | 646 | const dispY2 = this.selectBox.y + this.selectBox.height; |
| 603 | 647 | ||
| 604 | - // scale: displayed -> original | 648 | + // scale: displayed -> original (sửa lại để chính xác hơn) |
| 605 | const displayedWidth = this.$refs.pdfImage.clientWidth; | 649 | const displayedWidth = this.$refs.pdfImage.clientWidth; |
| 606 | const displayedHeight = this.$refs.pdfImage.clientHeight; | 650 | const displayedHeight = this.$refs.pdfImage.clientHeight; |
| 607 | const scaleX = this.imageWidth / displayedWidth; | 651 | const scaleX = this.imageWidth / displayedWidth; |
| ... | @@ -661,39 +705,76 @@ | ... | @@ -661,39 +705,76 @@ |
| 661 | applyManualMapping() { | 705 | applyManualMapping() { |
| 662 | if (!this.manualField) return; | 706 | if (!this.manualField) return; |
| 663 | const manualIndex = this.manualIndex; | 707 | const manualIndex = this.manualIndex; |
| 708 | + | ||
| 664 | const newBbox = this.ocrData[manualIndex].bbox; | 709 | const newBbox = this.ocrData[manualIndex].bbox; |
| 710 | + console.log(`33333 ${manualIndex} with field "${this.manualField}" new box ${newBbox}`); | ||
| 711 | + // console.log('Applying manual mapping for field:', this.manualField); | ||
| 712 | + // console.log('Manual bbox:', newBbox); | ||
| 665 | 713 | ||
| 666 | let combinedText = []; | 714 | let combinedText = []; |
| 715 | + let foundItems = []; | ||
| 716 | + | ||
| 717 | + // Tìm tất cả các box OCR nằm trong vùng manual | ||
| 667 | this.ocrData.forEach(item => { | 718 | this.ocrData.forEach(item => { |
| 668 | if (!item.isManual && this.isBoxInside(item.bbox, newBbox) && item.text.trim()) { | 719 | if (!item.isManual && this.isBoxInside(item.bbox, newBbox) && item.text.trim()) { |
| 669 | - const partial = this.getPartialText(item.text, item.bbox, newBbox); | 720 | + foundItems.push({ |
| 670 | - if (partial) combinedText.push(partial); | 721 | + text: item.text, |
| 671 | - // combinedText.push(item.text.trim()); | 722 | + bbox: item.bbox, |
| 723 | + index: this.ocrData.indexOf(item) | ||
| 724 | + }); | ||
| 725 | + } | ||
| 726 | + }); | ||
| 727 | + | ||
| 728 | + // console.log('Found OCR items in manual area:', foundItems); | ||
| 729 | + | ||
| 730 | + // Sắp xếp các item theo vị trí (từ trái sang phải, từ trên xuống dưới) | ||
| 731 | + foundItems.sort((a, b) => { | ||
| 732 | + // Ưu tiên theo Y trước (hàng), sau đó theo X (cột) | ||
| 733 | + if (Math.abs(a.bbox[1] - b.bbox[1]) < 20) { // Cùng hàng (tolerance 20px) | ||
| 734 | + return a.bbox[0] - b.bbox[0]; // Sắp xếp theo X | ||
| 672 | } | 735 | } |
| 736 | + return a.bbox[1] - b.bbox[1]; // Sắp xếp theo Y | ||
| 737 | + }); | ||
| 738 | + | ||
| 739 | + // Gộp text theo thứ tự đã sắp xếp | ||
| 740 | + foundItems.forEach(item => { | ||
| 741 | + combinedText.push(item.text.trim()); | ||
| 673 | }); | 742 | }); |
| 674 | 743 | ||
| 675 | const finalText = combinedText.join(" "); | 744 | const finalText = combinedText.join(" "); |
| 676 | - // this.ocrData[manualIndex].field = this.manualField; | 745 | + // console.log('Combined text:', finalText); |
| 677 | - // this.formData[this.manualField] = finalText; | ||
| 678 | - // this.activeIndex = manualIndex; | ||
| 679 | 746 | ||
| 747 | + // Gán field và text cho box manual | ||
| 680 | this.assignFieldToBox(manualIndex, this.manualField, finalText); | 748 | this.assignFieldToBox(manualIndex, this.manualField, finalText); |
| 681 | 749 | ||
| 682 | // Reset trạng thái chọn | 750 | // Reset trạng thái chọn |
| 683 | this.isMappingManually = false; | 751 | this.isMappingManually = false; |
| 684 | this.selectBox.show = false; | 752 | this.selectBox.show = false; |
| 685 | this.selectBox.showDropdown = false; | 753 | this.selectBox.showDropdown = false; |
| 686 | - // this.manualField = ""; | ||
| 687 | - // this.manualIndex = null; | ||
| 688 | }, | 754 | }, |
| 689 | 755 | ||
| 690 | isBoxInside(inner, outer) { | 756 | isBoxInside(inner, outer) { |
| 691 | - return !( | 757 | + // inner: bbox của OCR item [x1, y1, x2, y2] |
| 758 | + // outer: bbox của vùng manual [x1, y1, x2, y2] | ||
| 759 | + | ||
| 760 | + // Kiểm tra xem box OCR có nằm hoàn toàn trong vùng manual không | ||
| 761 | + const isFullyInside = ( | ||
| 762 | + inner[0] >= outer[0] && // left edge | ||
| 763 | + inner[1] >= outer[1] && // top edge | ||
| 764 | + inner[2] <= outer[2] && // right edge | ||
| 765 | + inner[3] <= outer[3] // bottom edge | ||
| 766 | + ); | ||
| 767 | + | ||
| 768 | + // Kiểm tra xem box OCR có giao nhau với vùng manual không | ||
| 769 | + const isOverlapping = !( | ||
| 692 | inner[2] < outer[0] || // box bên trái vùng chọn | 770 | inner[2] < outer[0] || // box bên trái vùng chọn |
| 693 | inner[0] > outer[2] || // box bên phải vùng chọn | 771 | inner[0] > outer[2] || // box bên phải vùng chọn |
| 694 | inner[3] < outer[1] || // box phía trên vùng chọn | 772 | inner[3] < outer[1] || // box phía trên vùng chọn |
| 695 | inner[1] > outer[3] // box phía dưới vùng chọn | 773 | inner[1] > outer[3] // box phía dưới vùng chọn |
| 696 | ); | 774 | ); |
| 775 | + | ||
| 776 | + // Trả về true nếu box OCR nằm hoàn toàn trong hoặc giao nhau đáng kể | ||
| 777 | + return isFullyInside || isOverlapping; | ||
| 697 | }, | 778 | }, |
| 698 | 779 | ||
| 699 | 780 | ||
| ... | @@ -704,6 +785,7 @@ | ... | @@ -704,6 +785,7 @@ |
| 704 | 785 | ||
| 705 | // Chiều rộng box OCR | 786 | // Chiều rộng box OCR |
| 706 | const boxWidth = x2 - x1; | 787 | const boxWidth = x2 - x1; |
| 788 | + const boxHeight = y2 - y1; | ||
| 707 | 789 | ||
| 708 | // Vị trí start và end tương đối trong text | 790 | // Vị trí start và end tương đối trong text |
| 709 | let startRatio = Math.max(0, (sx1 - x1) / boxWidth); | 791 | let startRatio = Math.max(0, (sx1 - x1) / boxWidth); |
| ... | @@ -712,7 +794,18 @@ | ... | @@ -712,7 +794,18 @@ |
| 712 | const startIndex = Math.floor(startRatio * text.length); | 794 | const startIndex = Math.floor(startRatio * text.length); |
| 713 | const endIndex = Math.ceil(endRatio * text.length); | 795 | const endIndex = Math.ceil(endRatio * text.length); |
| 714 | 796 | ||
| 715 | - return text.substring(startIndex, endIndex).trim(); | 797 | + const partialText = text.substring(startIndex, endIndex).trim(); |
| 798 | + | ||
| 799 | + // console.log('Partial text calculation:', { | ||
| 800 | + // originalText: text, | ||
| 801 | + // bbox: bbox, | ||
| 802 | + // selectBbox: selectBbox, | ||
| 803 | + // startRatio, endRatio, | ||
| 804 | + // startIndex, endIndex, | ||
| 805 | + // partialText | ||
| 806 | + // }); | ||
| 807 | + | ||
| 808 | + return partialText; | ||
| 716 | }, | 809 | }, |
| 717 | getSelectStyle(item) { | 810 | getSelectStyle(item) { |
| 718 | if (!this.imageWidth) return { position: 'absolute' }; | 811 | if (!this.imageWidth) return { position: 'absolute' }; |
| ... | @@ -729,6 +822,185 @@ | ... | @@ -729,6 +822,185 @@ |
| 729 | top: `${Math.round(y2 * scaleY)}px`, | 822 | top: `${Math.round(y2 * scaleY)}px`, |
| 730 | zIndex: 9999 | 823 | zIndex: 9999 |
| 731 | }; | 824 | }; |
| 825 | + }, | ||
| 826 | + | ||
| 827 | + // Debug method để kiểm tra tọa độ | ||
| 828 | + debugCoordinates() { | ||
| 829 | + console.log('=== DEBUG COORDINATES ==='); | ||
| 830 | + console.log('Image dimensions:', { | ||
| 831 | + natural: { width: this.imageWidth, height: this.imageHeight }, | ||
| 832 | + displayed: { | ||
| 833 | + width: this.$refs.pdfImage?.clientWidth, | ||
| 834 | + height: this.$refs.pdfImage?.clientHeight | ||
| 835 | + } | ||
| 836 | + }); | ||
| 837 | + | ||
| 838 | + console.log('Scale factors:', { | ||
| 839 | + scaleX: this.$refs.pdfImage ? this.$refs.pdfImage.clientWidth / this.imageWidth : 'N/A', | ||
| 840 | + scaleY: this.$refs.pdfImage ? this.$refs.pdfImage.clientHeight / this.imageHeight : 'N/A' | ||
| 841 | + }); | ||
| 842 | + | ||
| 843 | + console.log('OCR Data with coordinates:'); | ||
| 844 | + this.ocrData.forEach((item, index) => { | ||
| 845 | + if (!item.isDeleted) { | ||
| 846 | + console.log(`Item ${index}:`, { | ||
| 847 | + text: item.text, | ||
| 848 | + bbox: item.bbox, | ||
| 849 | + field: item.field, | ||
| 850 | + isManual: item.isManual | ||
| 851 | + }); | ||
| 852 | + } | ||
| 853 | + }); | ||
| 854 | + console.log('=== END DEBUG ==='); | ||
| 855 | + }, | ||
| 856 | + | ||
| 857 | + // Kiểm tra và sửa lại tọa độ của các box manual | ||
| 858 | + validateManualBoxes() { | ||
| 859 | + if (!this.imageWidth || !this.imageHeight) { | ||
| 860 | + console.log('validateManualBoxes: Image not loaded yet'); | ||
| 861 | + return; | ||
| 862 | + } | ||
| 863 | + | ||
| 864 | + this.ocrData.forEach((item, index) => { | ||
| 865 | + if (item.isManual && !item.isDeleted) { | ||
| 866 | + const [x1, y1, x2, y2] = item.bbox; | ||
| 867 | + | ||
| 868 | + const isValid = ( | ||
| 869 | + x1 >= 0 && y1 >= 0 && | ||
| 870 | + x2 > x1 && y2 > y1 && | ||
| 871 | + x2 <= this.imageWidth && y2 <= this.imageHeight | ||
| 872 | + ); | ||
| 873 | + | ||
| 874 | + if (!isValid) { | ||
| 875 | + // Thử sửa lại tọa độ nếu có thể | ||
| 876 | + this.fixManualBoxCoordinates(item, index); | ||
| 877 | + } | ||
| 878 | + } | ||
| 879 | + }); | ||
| 880 | + | ||
| 881 | + console.log('=== END VALIDATION ==='); | ||
| 882 | + | ||
| 883 | + // Force re-render để đảm bảo các box được vẽ đúng | ||
| 884 | + this.$nextTick(() => { | ||
| 885 | + this.$forceUpdate(); | ||
| 886 | + }); | ||
| 887 | + }, | ||
| 888 | + | ||
| 889 | + // Sửa lại tọa độ của box manual nếu bị lỗi | ||
| 890 | + fixManualBoxCoordinates(item, index) { | ||
| 891 | + console.log(`Attempting to fix manual box ${index}:`, item); | ||
| 892 | + | ||
| 893 | + // Nếu tọa độ âm, đặt về 0 | ||
| 894 | + let [x1, y1, x2, y2] = item.bbox; | ||
| 895 | + | ||
| 896 | + if (x1 < 0) x1 = 0; | ||
| 897 | + if (y1 < 0) y1 = 0; | ||
| 898 | + if (x2 <= x1) x2 = x1 + 100; // Tạo width mặc định | ||
| 899 | + if (y2 <= y1) y2 = y1 + 50; // Tạo height mặc định | ||
| 900 | + | ||
| 901 | + // Đảm bảo không vượt quá image bounds | ||
| 902 | + if (x2 > this.imageWidth) x2 = this.imageWidth; | ||
| 903 | + if (y2 > this.imageHeight) y2 = this.imageHeight; | ||
| 904 | + | ||
| 905 | + const fixedBbox = [x1, y1, x2, y2]; | ||
| 906 | + console.log(`Fixed bbox for manual box ${index}:`, { | ||
| 907 | + original: item.bbox, | ||
| 908 | + fixed: fixedBbox | ||
| 909 | + }); | ||
| 910 | + | ||
| 911 | + // Cập nhật tọa độ | ||
| 912 | + this.$set(this.ocrData[index], 'bbox', fixedBbox); | ||
| 913 | + }, | ||
| 914 | + | ||
| 915 | + // Test method để tạo box manual test | ||
| 916 | + testManualBox() { | ||
| 917 | + if (!this.imageWidth || !this.imageHeight) { | ||
| 918 | + alert('Image chưa được load. Vui lòng đợi image load xong.'); | ||
| 919 | + return; | ||
| 920 | + } | ||
| 921 | + | ||
| 922 | + // Tạo một box manual test ở giữa image | ||
| 923 | + const centerX = Math.round(this.imageWidth / 2); | ||
| 924 | + const centerY = Math.round(this.imageHeight / 2); | ||
| 925 | + const boxSize = 100; | ||
| 926 | + | ||
| 927 | + const testBbox = [ | ||
| 928 | + centerX - boxSize/2, // x1 | ||
| 929 | + centerY - boxSize/2, // y1 | ||
| 930 | + centerX + boxSize/2, // x2 | ||
| 931 | + centerY + boxSize/2 // y2 | ||
| 932 | + ]; | ||
| 933 | + | ||
| 934 | + console.log('Creating test manual box:', { | ||
| 935 | + bbox: testBbox, | ||
| 936 | + imageDimensions: { width: this.imageWidth, height: this.imageHeight } | ||
| 937 | + }); | ||
| 938 | + | ||
| 939 | + // Thêm box test | ||
| 940 | + this.ocrData.push({ | ||
| 941 | + text: "TEST BOX", | ||
| 942 | + bbox: testBbox, | ||
| 943 | + field: "test_field", | ||
| 944 | + isManual: true, | ||
| 945 | + showDelete: true, | ||
| 946 | + isDeleted: false, | ||
| 947 | + hideBorder: false | ||
| 948 | + }); | ||
| 949 | + | ||
| 950 | + // Force re-render | ||
| 951 | + this.$forceUpdate(); | ||
| 952 | + }, | ||
| 953 | + | ||
| 954 | + // Tạo box manual từ tọa độ trong DB | ||
| 955 | + createManualBoxFromDB(fieldName, coordinates, text) { | ||
| 956 | + if (!this.imageWidth || !this.imageHeight) { | ||
| 957 | + console.log('Cannot create manual box: Image not loaded'); | ||
| 958 | + return; | ||
| 959 | + } | ||
| 960 | + | ||
| 961 | + // Parse coordinates từ string "x1,y1,x2,y2" | ||
| 962 | + let coords; | ||
| 963 | + if (typeof coordinates === 'string') { | ||
| 964 | + coords = coordinates.split(',').map(Number); | ||
| 965 | + } else if (Array.isArray(coordinates)) { | ||
| 966 | + coords = coordinates; | ||
| 967 | + } else { | ||
| 968 | + console.error('Invalid coordinates format:', coordinates); | ||
| 969 | + return; | ||
| 970 | + } | ||
| 971 | + | ||
| 972 | + const [x1, y1, x2, y2] = coords; | ||
| 973 | + | ||
| 974 | + console.log('Creating manual box from DB:', { | ||
| 975 | + fieldName, | ||
| 976 | + coordinates, | ||
| 977 | + parsed: coords, | ||
| 978 | + imageDimensions: { width: this.imageWidth, height: this.imageHeight } | ||
| 979 | + }); | ||
| 980 | + | ||
| 981 | + // Kiểm tra tọa độ có hợp lệ không | ||
| 982 | + if (x1 >= 0 && y1 >= 0 && x2 > x1 && y2 > y1 && | ||
| 983 | + x2 <= this.imageWidth && y2 <= this.imageHeight) { | ||
| 984 | + | ||
| 985 | + // Tạo box manual | ||
| 986 | + const manualBox = { | ||
| 987 | + text: text || '', | ||
| 988 | + bbox: coords, | ||
| 989 | + field: fieldName, | ||
| 990 | + isManual: true, | ||
| 991 | + showDelete: true, | ||
| 992 | + isDeleted: false, | ||
| 993 | + hideBorder: false | ||
| 994 | + }; | ||
| 995 | + | ||
| 996 | + this.ocrData.push(manualBox); | ||
| 997 | + console.log('Manual box created successfully:', manualBox); | ||
| 998 | + | ||
| 999 | + // Force re-render | ||
| 1000 | + this.$forceUpdate(); | ||
| 1001 | + } else { | ||
| 1002 | + console.warn('Invalid coordinates for manual box:', coords); | ||
| 1003 | + } | ||
| 732 | } | 1004 | } |
| 733 | 1005 | ||
| 734 | } | 1006 | } | ... | ... |
-
Please register or sign in to post a comment