ivanm151 commited on
Commit
b7d2f0a
·
1 Parent(s): 9672426

1st classifier upd

Browse files
Files changed (4) hide show
  1. app.py +6 -3
  2. models.py +1 -1
  3. utils.py +39 -63
  4. weights/{class1.pth → class.pth} +1 -1
app.py CHANGED
@@ -78,7 +78,9 @@ async def predict2(
78
  mask_256 = decode_base64_mask(mask_256_base64)
79
 
80
  # 3. Letterbox + маска + crop + resize до 100×100
81
- cropped_100 = apply_mask_and_crop_letterbox(original_np, mask_256)
 
 
82
 
83
  # 4. Препроцессинг для классификатора
84
  input_tensor = preprocess_for_classifier(cropped_100).unsqueeze(0).to(DEVICE)
@@ -112,10 +114,11 @@ async def predict3(
112
  mask_256 = decode_base64_mask(mask_256_base64)
113
 
114
  # Вырезаем и готовим 224×224
115
- cropped_224 = apply_mask_and_crop_letterbox_224(
116
  original_np,
117
  mask_256,
118
- margin_ratio=0.05, # подбери под свои тесты
 
119
  bg_color=(255, 255, 255)
120
  )
121
 
 
78
  mask_256 = decode_base64_mask(mask_256_base64)
79
 
80
  # 3. Letterbox + маска + crop + resize до 100×100
81
+ cropped_100 = apply_mask_and_crop_letterbox(original_np, mask_256, margin_ratio=0.02,
82
+ target_size=100,
83
+ bg_color=(255, 255, 255))
84
 
85
  # 4. Препроцессинг для классификатора
86
  input_tensor = preprocess_for_classifier(cropped_100).unsqueeze(0).to(DEVICE)
 
114
  mask_256 = decode_base64_mask(mask_256_base64)
115
 
116
  # Вырезаем и готовим 224×224
117
+ cropped_224 = apply_mask_and_crop_letterbox(
118
  original_np,
119
  mask_256,
120
+ margin_ratio=0.05,
121
+ target_size=224,
122
  bg_color=(255, 255, 255)
123
  )
124
 
models.py CHANGED
@@ -24,7 +24,7 @@ def load_model1(weights_path='weights/seg.pth'):
24
  return model1
25
 
26
 
27
- def load_model2(weights_path='weights/class1.pth'):
28
  global model2
29
  if model2 is None:
30
  model2 = models.mobilenet_v2(pretrained=False)
 
24
  return model1
25
 
26
 
27
+ def load_model2(weights_path='weights/class.pth'):
28
  global model2
29
  if model2 is None:
30
  model2 = models.mobilenet_v2(pretrained=False)
utils.py CHANGED
@@ -44,8 +44,7 @@ def mask_to_base64(mask: np.ndarray) -> str:
44
  # ────────────────────────────────────────────────
45
 
46
  # Новые для классификации
47
- FRUIT_CLASSES = ['apple', 'banana', 'orange', 'grape', 'strawberry',
48
- 'tomato', 'pear', 'peach', 'cherry', 'lemon']
49
 
50
 
51
  def decode_base64_mask(base64_str: str) -> np.ndarray:
@@ -81,36 +80,56 @@ def letterbox_resize(img: np.ndarray, target_size: int = 256) -> tuple[np.ndarra
81
  return padded, scale, (top, bottom, left, right)
82
 
83
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
84
  def apply_mask_and_crop_letterbox(
85
- orig_img: np.ndarray, # оригинал любой размер
86
- mask_256: np.ndarray # маска 256×256 [0..1]
 
 
 
87
  ) -> np.ndarray:
88
- """
89
- 1. Делаем letterbox-версию оригинала 256×256
90
- 2. Применяем маску
91
- 3. Находим bbox
92
- 4. Вырезаем + margin
93
- 5. Ресайзим до 100×100
94
- """
95
- letterbox_img, scale, paddings = letterbox_resize(orig_img, 256)
96
  top, bottom, left, right = paddings
97
 
98
- # Маска уже 256×256 — применяем напрямую
99
  masked = letterbox_img.copy()
100
- masked[mask_256 < 0.5] = [255, 255, 255]
101
 
102
- # Находим контуры / bbox
103
  mask_bin = (mask_256 > 0.5).astype(np.uint8) * 255
104
  contours, _ = cv2.findContours(mask_bin, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
105
 
106
  if not contours:
107
- return np.zeros((100, 100, 3), dtype=np.uint8)
108
 
109
  cnt = max(contours, key=cv2.contourArea)
110
  x, y, bw, bh = cv2.boundingRect(cnt)
111
 
112
- # margin ~10%
113
- margin = int(max(bw, bh) * 0.02)
114
  x1 = max(0, x - margin)
115
  y1 = max(0, y - margin)
116
  x2 = min(256, x + bw + margin)
@@ -118,8 +137,8 @@ def apply_mask_and_crop_letterbox(
118
 
119
  cropped = masked[y1:y2, x1:x2]
120
 
121
- # Финальный ресайз до 100×100 для классификатора
122
- final = cv2.resize(cropped, (100, 100), interpolation=cv2.INTER_AREA)
123
 
124
  return final
125
 
@@ -149,46 +168,3 @@ def preprocess_for_freshness(img_224: np.ndarray) -> torch.Tensor:
149
  transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
150
  ])
151
  return transform(img_224)
152
-
153
-
154
- def apply_mask_and_crop_letterbox_224(
155
- orig_img: np.ndarray,
156
- mask_256: np.ndarray,
157
- margin_ratio: float = 0.05, # можно подкрутить
158
- bg_color: tuple = (255, 255, 255) # белый фон — важно!
159
- ) -> np.ndarray:
160
- """
161
- Аналог apply_mask_and_crop_letterbox, но для 224×224
162
- """
163
- # Letterbox до 224×224
164
- letterbox_img, scale, paddings = letterbox_resize(orig_img, target_size=224)
165
- top, bottom, left, right = paddings
166
-
167
- # Применяем маску (маска 256→ресайзим до 224)
168
- mask_resized = cv2.resize(mask_256, (224, 224), interpolation=cv2.INTER_NEAREST)
169
-
170
- masked = letterbox_img.copy()
171
- masked[mask_resized < 0.5] = bg_color # белый фон
172
-
173
- # Контуры
174
- mask_bin = (mask_resized > 0.5).astype(np.uint8) * 255
175
- contours, _ = cv2.findContours(mask_bin, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
176
-
177
- if not contours:
178
- return np.full((224, 224, 3), bg_color, dtype=np.uint8)
179
-
180
- cnt = max(contours, key=cv2.contourArea)
181
- x, y, bw, bh = cv2.boundingRect(cnt)
182
-
183
- margin = int(max(bw, bh) * margin_ratio)
184
- x1 = max(0, x - margin)
185
- y1 = max(0, y - margin)
186
- x2 = min(224, x + bw + margin)
187
- y2 = min(224, y + bh + margin)
188
-
189
- cropped = masked[y1:y2, x1:x2]
190
-
191
- # Финальный resize до 224×224 (если обрезали меньше)
192
- final = cv2.resize(cropped, (224, 224), interpolation=cv2.INTER_AREA)
193
-
194
- return final
 
44
  # ────────────────────────────────────────────────
45
 
46
  # Новые для классификации
47
+ FRUIT_CLASSES = ['apple', 'banana', 'orange', 'strawberry', 'pear', 'lemon', 'cucumber', 'plum', 'raspberry', 'watermelon']
 
48
 
49
 
50
  def decode_base64_mask(base64_str: str) -> np.ndarray:
 
80
  return padded, scale, (top, bottom, left, right)
81
 
82
 
83
+ def letterbox_any_size(
84
+ img: np.ndarray,
85
+ target_size: int = 100, # или 224
86
+ bg_color: tuple = (255, 255, 255)
87
+ ) -> np.ndarray:
88
+ """ Универсальный letterbox для любого входного изображения """
89
+ h, w = img.shape[:2]
90
+ scale = min(target_size / h, target_size / w)
91
+ new_h, new_w = int(h * scale), int(w * scale)
92
+
93
+ resized = cv2.resize(img, (new_w, new_h), interpolation=cv2.INTER_AREA)
94
+
95
+ pad_h = target_size - new_h
96
+ pad_w = target_size - new_w
97
+ top = pad_h // 2
98
+ bottom = pad_h - top
99
+ left = pad_w // 2
100
+ right = pad_w - left
101
+
102
+ padded = cv2.copyMakeBorder(
103
+ resized, top, bottom, left, right,
104
+ cv2.BORDER_CONSTANT, value=bg_color
105
+ )
106
+ return padded
107
+
108
+
109
  def apply_mask_and_crop_letterbox(
110
+ orig_img: np.ndarray,
111
+ mask_256: np.ndarray,
112
+ margin_ratio: float = 0.02,
113
+ target_size: int = 100,
114
+ bg_color: tuple = (255, 255, 255)
115
  ) -> np.ndarray:
116
+ # Letterbox оригинала до 256×256 (для совместимости с маской)
117
+ letterbox_img, _, paddings = letterbox_resize(orig_img, target_size=256)
 
 
 
 
 
 
118
  top, bottom, left, right = paddings
119
 
 
120
  masked = letterbox_img.copy()
121
+ masked[mask_256 < 0.5] = bg_color
122
 
 
123
  mask_bin = (mask_256 > 0.5).astype(np.uint8) * 255
124
  contours, _ = cv2.findContours(mask_bin, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
125
 
126
  if not contours:
127
+ return np.full((target_size, target_size, 3), bg_color, dtype=np.uint8)
128
 
129
  cnt = max(contours, key=cv2.contourArea)
130
  x, y, bw, bh = cv2.boundingRect(cnt)
131
 
132
+ margin = int(max(bw, bh) * margin_ratio)
 
133
  x1 = max(0, x - margin)
134
  y1 = max(0, y - margin)
135
  x2 = min(256, x + bw + margin)
 
137
 
138
  cropped = masked[y1:y2, x1:x2]
139
 
140
+ # Самое важное: letterbox вместо force-resize
141
+ final = letterbox_any_size(cropped, target_size=target_size, bg_color=bg_color)
142
 
143
  return final
144
 
 
168
  transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
169
  ])
170
  return transform(img_224)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
weights/{class1.pth → class.pth} RENAMED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:c5767f1246bfe8ee0077a0eefda6c8a1a66e8639de3fc1d94bacf7254633a5f2
3
  size 9205515
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:0720a4afc5d0649b9af6e1532947af363f7230276172f6345aae6b951be071e5
3
  size 9205515