Nguyễn Bá Hùng commited on
Commit
6a6419e
·
1 Parent(s): 7f2e279
Files changed (2) hide show
  1. label_viewer.py +2 -2
  2. templates/index.html +223 -4
label_viewer.py CHANGED
@@ -12,8 +12,8 @@ app = Flask(__name__)
12
 
13
  # Cấu hình đường dẫn
14
  BASE_DIR = Path(__file__).parent.parent # Lên một cấp từ danh_nhan về project_ss
15
- IMAGES_DIR = BASE_DIR / "data" / "images_crop" / "train"
16
- LABELS_DIR = BASE_DIR / "data" / "labels_crop" / "train"
17
 
18
  # Danh sách các class
19
  CLASSES = {
 
12
 
13
  # Cấu hình đường dẫn
14
  BASE_DIR = Path(__file__).parent.parent # Lên một cấp từ danh_nhan về project_ss
15
+ IMAGES_DIR = BASE_DIR / "data" / "images_crop" / "val"
16
+ LABELS_DIR = BASE_DIR / "data" / "labels_crop" / "val"
17
 
18
  # Danh sách các class
19
  CLASSES = {
templates/index.html CHANGED
@@ -78,15 +78,67 @@
78
  background: #f8f9fa;
79
  border-radius: 8px;
80
  position: relative;
81
- overflow: hidden;
 
 
 
 
 
82
  }
83
 
84
  .image-container img {
85
- max-width: 100%;
86
- max-height: 100%;
87
  object-fit: contain;
88
  border-radius: 8px;
89
  box-shadow: 0 4px 15px rgba(0,0,0,0.1);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90
  }
91
 
92
  .label-container {
@@ -336,9 +388,22 @@
336
  <h2 class="panel-title">🖼️ Ảnh</h2>
337
  </div>
338
  <div class="image-name" id="imageName">Chưa chọn ảnh</div>
339
- <div class="image-container">
340
  <img id="currentImage" src="" alt="Chọn ảnh để xem" style="display: none;">
341
  <div id="imagePlaceholder" class="loading">Chọn ảnh từ danh sách bên trên</div>
 
 
 
 
 
 
 
 
 
 
 
 
 
342
  </div>
343
  </div>
344
 
@@ -390,6 +455,9 @@
390
  let imageFiles = {{ image_files | tojson }};
391
  let currentImageIndex = -1;
392
  let currentImageName = '';
 
 
 
393
 
394
  // Khởi tạo
395
  document.addEventListener('DOMContentLoaded', function() {
@@ -399,8 +467,135 @@
399
  loadImage(selectedImage);
400
  }
401
  });
 
 
 
402
  });
403
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
404
  function loadImage(imageName) {
405
  currentImageName = imageName;
406
  currentImageIndex = imageFiles.indexOf(imageName);
@@ -413,6 +608,18 @@
413
  // Hiển thị ảnh
414
  const img = document.getElementById('currentImage');
415
  const placeholder = document.getElementById('imagePlaceholder');
 
 
 
 
 
 
 
 
 
 
 
 
416
 
417
  img.src = `/images/${imageName}`;
418
  img.style.display = 'block';
@@ -600,6 +807,18 @@
600
  } else if (e.key === 'Enter' && e.target.id === 'labelEditor') {
601
  e.preventDefault();
602
  saveAndNext();
 
 
 
 
 
 
 
 
 
 
 
 
603
  }
604
  });
605
  </script>
 
78
  background: #f8f9fa;
79
  border-radius: 8px;
80
  position: relative;
81
+ overflow: auto;
82
+ cursor: grab;
83
+ }
84
+
85
+ .image-container:active {
86
+ cursor: grabbing;
87
  }
88
 
89
  .image-container img {
90
+ max-width: none;
91
+ max-height: none;
92
  object-fit: contain;
93
  border-radius: 8px;
94
  box-shadow: 0 4px 15px rgba(0,0,0,0.1);
95
+ transition: transform 0.1s ease;
96
+ }
97
+
98
+ .zoom-controls {
99
+ position: absolute;
100
+ top: 10px;
101
+ right: 10px;
102
+ display: flex;
103
+ flex-direction: column;
104
+ gap: 5px;
105
+ background: rgba(255, 255, 255, 0.9);
106
+ border-radius: 8px;
107
+ padding: 5px;
108
+ box-shadow: 0 2px 10px rgba(0,0,0,0.1);
109
+ }
110
+
111
+ .zoom-btn {
112
+ width: 35px;
113
+ height: 35px;
114
+ border: none;
115
+ border-radius: 6px;
116
+ background: #667eea;
117
+ color: white;
118
+ font-size: 16px;
119
+ font-weight: bold;
120
+ cursor: pointer;
121
+ display: flex;
122
+ align-items: center;
123
+ justify-content: center;
124
+ transition: all 0.2s;
125
+ }
126
+
127
+ .zoom-btn:hover {
128
+ background: #5a6fd8;
129
+ transform: scale(1.1);
130
+ }
131
+
132
+ .zoom-info {
133
+ position: absolute;
134
+ top: 10px;
135
+ left: 10px;
136
+ background: rgba(0, 0, 0, 0.7);
137
+ color: white;
138
+ padding: 5px 10px;
139
+ border-radius: 6px;
140
+ font-size: 12px;
141
+ font-weight: 500;
142
  }
143
 
144
  .label-container {
 
388
  <h2 class="panel-title">🖼️ Ảnh</h2>
389
  </div>
390
  <div class="image-name" id="imageName">Chưa chọn ảnh</div>
391
+ <div class="image-container" id="imageContainer">
392
  <img id="currentImage" src="" alt="Chọn ảnh để xem" style="display: none;">
393
  <div id="imagePlaceholder" class="loading">Chọn ảnh từ danh sách bên trên</div>
394
+
395
+ <!-- Zoom controls -->
396
+ <div class="zoom-controls" id="zoomControls" style="display: none;">
397
+ <button class="zoom-btn" onclick="zoomIn()" title="Phóng to">+</button>
398
+ <button class="zoom-btn" onclick="zoomOut()" title="Thu nhỏ">−</button>
399
+ <button class="zoom-btn" onclick="resetZoom()" title="Kích thước gốc">⌂</button>
400
+ <button class="zoom-btn" onclick="fitToContainer()" title="Vừa khung">⊞</button>
401
+ </div>
402
+
403
+ <!-- Zoom info -->
404
+ <div class="zoom-info" id="zoomInfo" style="display: none;">
405
+ <span id="zoomLevel">100%</span>
406
+ </div>
407
  </div>
408
  </div>
409
 
 
455
  let imageFiles = {{ image_files | tojson }};
456
  let currentImageIndex = -1;
457
  let currentImageName = '';
458
+ let currentZoom = 1;
459
+ let isDragging = false;
460
+ let startX, startY, scrollLeft, scrollTop;
461
 
462
  // Khởi tạo
463
  document.addEventListener('DOMContentLoaded', function() {
 
467
  loadImage(selectedImage);
468
  }
469
  });
470
+
471
+ // Thêm event listeners cho drag và zoom
472
+ setupImageInteraction();
473
  });
474
 
475
+ function setupImageInteraction() {
476
+ const imageContainer = document.getElementById('imageContainer');
477
+ const image = document.getElementById('currentImage');
478
+
479
+ // Mouse wheel zoom
480
+ imageContainer.addEventListener('wheel', function(e) {
481
+ e.preventDefault();
482
+ const delta = e.deltaY > 0 ? -0.1 : 0.1;
483
+ const newZoom = Math.max(0.1, Math.min(5, currentZoom + delta));
484
+ setZoom(newZoom);
485
+ });
486
+
487
+ // Drag functionality
488
+ imageContainer.addEventListener('mousedown', function(e) {
489
+ if (e.target === image) {
490
+ isDragging = true;
491
+ startX = e.pageX - imageContainer.offsetLeft;
492
+ startY = e.pageY - imageContainer.offsetTop;
493
+ scrollLeft = imageContainer.scrollLeft;
494
+ scrollTop = imageContainer.scrollTop;
495
+ imageContainer.style.cursor = 'grabbing';
496
+ }
497
+ });
498
+
499
+ document.addEventListener('mousemove', function(e) {
500
+ if (!isDragging) return;
501
+ e.preventDefault();
502
+ const x = e.pageX - imageContainer.offsetLeft;
503
+ const y = e.pageY - imageContainer.offsetTop;
504
+ const walkX = (x - startX) * 2;
505
+ const walkY = (y - startY) * 2;
506
+ imageContainer.scrollLeft = scrollLeft - walkX;
507
+ imageContainer.scrollTop = scrollTop - walkY;
508
+ });
509
+
510
+ document.addEventListener('mouseup', function() {
511
+ isDragging = false;
512
+ imageContainer.style.cursor = 'grab';
513
+ });
514
+
515
+ // Touch events for mobile
516
+ let lastTouchDistance = 0;
517
+
518
+ imageContainer.addEventListener('touchstart', function(e) {
519
+ if (e.touches.length === 2) {
520
+ lastTouchDistance = getTouchDistance(e.touches);
521
+ }
522
+ });
523
+
524
+ imageContainer.addEventListener('touchmove', function(e) {
525
+ e.preventDefault();
526
+ if (e.touches.length === 2) {
527
+ const currentDistance = getTouchDistance(e.touches);
528
+ const delta = currentDistance - lastTouchDistance;
529
+ const zoomDelta = delta * 0.001;
530
+ const newZoom = Math.max(0.1, Math.min(5, currentZoom + zoomDelta));
531
+ setZoom(newZoom);
532
+ lastTouchDistance = currentDistance;
533
+ }
534
+ });
535
+ }
536
+
537
+ function getTouchDistance(touches) {
538
+ const dx = touches[0].clientX - touches[1].clientX;
539
+ const dy = touches[0].clientY - touches[1].clientY;
540
+ return Math.sqrt(dx * dx + dy * dy);
541
+ }
542
+
543
+ function setZoom(zoom) {
544
+ currentZoom = zoom;
545
+ const image = document.getElementById('currentImage');
546
+ image.style.transform = `scale(${zoom})`;
547
+
548
+ // Update zoom info
549
+ document.getElementById('zoomLevel').textContent = Math.round(zoom * 100) + '%';
550
+ }
551
+
552
+ function zoomIn() {
553
+ const newZoom = Math.min(5, currentZoom * 1.2);
554
+ setZoom(newZoom);
555
+ }
556
+
557
+ function zoomOut() {
558
+ const newZoom = Math.max(0.1, currentZoom / 1.2);
559
+ setZoom(newZoom);
560
+ }
561
+
562
+ function resetZoom() {
563
+ setZoom(1);
564
+ centerImage();
565
+ }
566
+
567
+ function fitToContainer() {
568
+ const image = document.getElementById('currentImage');
569
+ const container = document.getElementById('imageContainer');
570
+
571
+ if (image.naturalWidth && image.naturalHeight) {
572
+ const containerRatio = container.clientWidth / container.clientHeight;
573
+ const imageRatio = image.naturalWidth / image.naturalHeight;
574
+
575
+ let zoom;
576
+ if (imageRatio > containerRatio) {
577
+ zoom = (container.clientWidth - 40) / image.naturalWidth;
578
+ } else {
579
+ zoom = (container.clientHeight - 40) / image.naturalHeight;
580
+ }
581
+
582
+ setZoom(zoom);
583
+ centerImage();
584
+ }
585
+ }
586
+
587
+ function centerImage() {
588
+ const container = document.getElementById('imageContainer');
589
+ const image = document.getElementById('currentImage');
590
+
591
+ setTimeout(() => {
592
+ const scrollX = (image.offsetWidth * currentZoom - container.clientWidth) / 2;
593
+ const scrollY = (image.offsetHeight * currentZoom - container.clientHeight) / 2;
594
+ container.scrollLeft = Math.max(0, scrollX);
595
+ container.scrollTop = Math.max(0, scrollY);
596
+ }, 50);
597
+ }
598
+
599
  function loadImage(imageName) {
600
  currentImageName = imageName;
601
  currentImageIndex = imageFiles.indexOf(imageName);
 
608
  // Hiển thị ảnh
609
  const img = document.getElementById('currentImage');
610
  const placeholder = document.getElementById('imagePlaceholder');
611
+ const zoomControls = document.getElementById('zoomControls');
612
+ const zoomInfo = document.getElementById('zoomInfo');
613
+
614
+ img.onload = function() {
615
+ // Reset zoom khi load ảnh mới
616
+ setZoom(1);
617
+ centerImage();
618
+
619
+ // Show zoom controls
620
+ zoomControls.style.display = 'flex';
621
+ zoomInfo.style.display = 'block';
622
+ };
623
 
624
  img.src = `/images/${imageName}`;
625
  img.style.display = 'block';
 
807
  } else if (e.key === 'Enter' && e.target.id === 'labelEditor') {
808
  e.preventDefault();
809
  saveAndNext();
810
+ } else if (e.key === '=' || e.key === '+') {
811
+ // Zoom in với phím +
812
+ zoomIn();
813
+ } else if (e.key === '-' || e.key === '_') {
814
+ // Zoom out với phím -
815
+ zoomOut();
816
+ } else if (e.key === '0' && !e.ctrlKey) {
817
+ // Reset zoom với phím 0
818
+ resetZoom();
819
+ } else if (e.key === '9') {
820
+ // Fit to container với phím 9
821
+ fitToContainer();
822
  }
823
  });
824
  </script>