Spaces:
Sleeping
Sleeping
Hikity
feat: implement currency classification pipeline with EfficientNet-B0 and model training utilities
2ba1249 Download app/classifier.py from Hikity/kawach-classifier: direct link, hf CLI and curl.
- Browser
- Download file 4.53 kB
-
https://huggingface.co/spaces/Hikity/kawach-classifier/resolve/main/app/classifier.py
- Command line
-
hf download hf://spaces/Hikity/kawach-classifier/app/classifier.py
-
curl -L -o classifier.py https://huggingface.co/spaces/Hikity/kawach-classifier/resolve/main/app/classifier.py
4.53 kB
| import numpy as np | |
| import torch | |
| import cv2 | |
| from torchvision.transforms import Normalize | |
| try: | |
| from albumentations.augmentations.functional import image_compression | |
| except ImportError: | |
| try: | |
| from albumentations.functional import image_compression | |
| except ImportError: | |
| def image_compression(img, *args, **kwargs): | |
| return img | |
| mean = [0.485, 0.456, 0.406] | |
| std = [0.229, 0.224, 0.225] | |
| normalize_transform = Normalize(mean, std) | |
| def confident_strategy(pred, t=0.8): | |
| pred = np.array(pred) | |
| sz = len(pred) | |
| fakes = np.count_nonzero(pred > t) | |
| # If >40% of frames are fake and we have at least 11 fake frames | |
| if fakes > sz // 2.5 and fakes > 11: | |
| return np.mean(pred[pred > t]) | |
| elif np.count_nonzero(pred < 0.2) > 0.9 * sz: | |
| return np.mean(pred[pred < 0.2]) | |
| else: | |
| return np.mean(pred) | |
| def put_to_center(img, input_size): | |
| img = img[:input_size, :input_size] | |
| image = np.zeros((input_size, input_size, 3), dtype=np.uint8) | |
| start_w = (input_size - img.shape[1]) // 2 | |
| start_h = (input_size - img.shape[0]) // 2 | |
| image[start_h:start_h + img.shape[0], start_w: start_w + img.shape[1], :] = img | |
| return image | |
| def isotropically_resize_image(img, size, interpolation_down=cv2.INTER_AREA, interpolation_up=cv2.INTER_CUBIC): | |
| h, w = img.shape[:2] | |
| if max(w, h) == size: | |
| return img | |
| if w > h: | |
| scale = size / w | |
| h = h * scale | |
| w = size | |
| else: | |
| scale = size / h | |
| w = w * scale | |
| h = size | |
| interpolation = interpolation_up if scale > 1 else interpolation_down | |
| resized = cv2.resize(img, (int(w), int(h)), interpolation=interpolation) | |
| return resized | |
| import os | |
| # Inference chunk size: how many face crops go through the B7 ensemble at | |
| # once. Chunking changes peak memory only, never the prediction (same faces, | |
| # same averaging strategy). 8 keeps a dual-B7 pass under ~2GB on CPU; raise it | |
| # via env on machines with headroom. | |
| INFER_CHUNK = int(os.environ.get("INFER_BATCH_SIZE", "8")) | |
| def predict_on_video(face_extractor, video_path, batch_size, input_size, models, strategy=confident_strategy, | |
| apply_compression=False, device=None): | |
| if device is None: | |
| device = "cuda" if torch.cuda.is_available() else "cpu" | |
| max_faces = batch_size * 4 | |
| try: | |
| faces = face_extractor.process_video(video_path) | |
| faces_detected = 0 | |
| frames_analyzed = len(faces) | |
| if len(faces) > 0: | |
| crops = [] | |
| for frame_data in faces: | |
| faces_detected += len(frame_data["faces"]) | |
| for face in frame_data["faces"]: | |
| if len(crops) >= max_faces: | |
| continue | |
| resized_face = isotropically_resize_image(face, input_size) | |
| resized_face = put_to_center(resized_face, input_size) | |
| if apply_compression: | |
| resized_face = image_compression(resized_face, quality=90, image_type=".jpg") | |
| crops.append(resized_face) | |
| n = len(crops) | |
| if n > 0: | |
| x = np.stack(crops) # only actual faces — no fixed 128-slot buffer | |
| with torch.no_grad(): | |
| preds = [] | |
| for model in models: | |
| frame_preds = [] | |
| # Chunked inference: identical output to a single big | |
| # batch, but peak memory stays bounded on 8GB machines | |
| # and free-tier CPU hosts. | |
| for start in range(0, n, INFER_CHUNK): | |
| chunk = torch.tensor(x[start:start + INFER_CHUNK], device=device).float() | |
| chunk = chunk.permute((0, 3, 1, 2)) | |
| for i in range(len(chunk)): | |
| chunk[i] = normalize_transform(chunk[i] / 255.) | |
| model_input = chunk.half() if device == "cuda" else chunk.float() | |
| y_pred = torch.sigmoid(model(model_input)) | |
| frame_preds.extend(y_pred.view(-1).cpu().numpy().tolist()) | |
| del chunk, model_input, y_pred | |
| preds.append(strategy(np.array(frame_preds))) | |
| return float(np.mean(preds)), faces_detected, frames_analyzed | |
| except Exception as e: | |
| print("Prediction error on video %s: %s" % (video_path, str(e))) | |
| return 0.5, 0, 0 | |