UserPollo commited on
Commit
151f8a5
·
verified ·
1 Parent(s): 26d751c

Update README.md

Browse files
Files changed (1) hide show
  1. README.md +123 -22
README.md CHANGED
@@ -1,23 +1,25 @@
1
  ---
2
  license: apache-2.0
3
  tags:
4
- - vision
5
- - image-classification
6
- - dinov2
7
- - moire-detection
8
- - screen-detection
9
- - anti-spoofing
10
- - digital-forensics
11
  datasets:
12
- - soumikrakshit/uhdm-dataset
13
  pipeline_tag: image-classification
 
 
14
  ---
15
 
16
  # Dual-Branch DINOv2 Moiré & Screen Recapture Detector
17
 
18
  A robust digital forensics classifier built on **DINOv2 (with registers)** designed to detect screen recaptures and Moiré patterns.
19
 
20
- By combining an un-resized native crop (capturing high-frequency pixel interference) with a global thumbnail (capturing screen-wide periodic banding), this model overcomes the traditional scale dilemma in Moiré detection.
21
 
22
  ---
23
 
@@ -29,21 +31,120 @@ By combining an un-resized native crop (capturing high-frequency pixel interfere
29
 
30
  ---
31
 
32
- ## 🧠 Architecture Highlights
33
-
34
- 1. **Dual-Branch Input**:
35
- * **Local Branch (Native Resolution Crop):** Recropped directly at $224 \times 224$ from native image resolution without prior resizing, preventing antialiasing from washing out high-frequency Moiré lines.
36
- * **Global Branch (Thumbnail View):** Resized to $256 \times 256$ and cropped to $224 \times 224$ to detect macroscopic periodic interference patterns.
37
- 2. **Feature Concatenation ($3072$-d)**:
38
- * Combines `[CLS_local, PatchMean_local, CLS_global, PatchMean_global]`, ignoring internal register tokens.
39
- 3. **Partial Backbone Fine-Tuning**:
40
- * Top 2 transformer layers adapted to Moiré patterns with differential learning rates, while earlier feature representations remain frozen.
41
-
42
- ---
43
-
44
  ## 🚀 Quick Start & Inference
45
 
46
  ### 1. Requirements
47
 
 
 
 
 
48
  ```bash
49
- pip install torch torchvision transformers pillow
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
  license: apache-2.0
3
  tags:
4
+ - vision
5
+ - image-classification
6
+ - dinov2
7
+ - moire-detection
8
+ - screen-detection
9
+ - anti-spoofing
10
+ - digital-forensics
11
  datasets:
12
+ - soumikrakshit/uhdm-dataset
13
  pipeline_tag: image-classification
14
+ base_model:
15
+ - facebook/dinov2-with-registers-base
16
  ---
17
 
18
  # Dual-Branch DINOv2 Moiré & Screen Recapture Detector
19
 
20
  A robust digital forensics classifier built on **DINOv2 (with registers)** designed to detect screen recaptures and Moiré patterns.
21
 
22
+ Optimized for anti-spoofing pipelines and automated asset valuation platforms, this model overcomes the traditional scale dilemma in Moiré detection by combining an un-resized native crop (capturing high-frequency pixel interference) with a global thumbnail (capturing screen-wide periodic banding).
23
 
24
  ---
25
 
 
31
 
32
  ---
33
 
 
 
 
 
 
 
 
 
 
 
 
 
34
  ## 🚀 Quick Start & Inference
35
 
36
  ### 1. Requirements
37
 
38
+ If you are running this in **Google Colab**, you do not need to run `pip install` for most of these packages, as PyTorch, Transformers, and Pillow are pre-installed. You only need to ensure `huggingface-hub` is up to date.
39
+
40
+ For local environments, install the dependencies:
41
+
42
  ```bash
43
+ pip install torch torchvision transformers pillow huggingface-hub requests
44
+ ```
45
+
46
+ ### 2. Inference Script
47
+
48
+ ```python
49
+ import torch
50
+ import torch.nn as nn
51
+ import json
52
+ import requests
53
+ from PIL import Image
54
+ from torchvision import transforms
55
+ from transformers import AutoModel
56
+ from huggingface_hub import hf_hub_download
57
+
58
+ # 1. Download weights and classes from the Hub
59
+ repo_id = "UserPollo/moire-pattern-detector"
60
+ backbone_ckpt = hf_hub_download(repo_id=repo_id, filename="best_screen_detector_backbone.pt")
61
+ mlp_ckpt = hf_hub_download(repo_id=repo_id, filename="best_screen_detector_mlp.pt")
62
+ classes_file = hf_hub_download(repo_id=repo_id, filename="classes.json")
63
+
64
+ with open(classes_file, "r") as f:
65
+ classes = json.load(f)
66
+
67
+ # 2. Define the custom MLP Head
68
+ class ScreenDetectorMLP(nn.Module):
69
+ def __init__(self, input_size=3072, hidden_size=256, num_classes=2, dropout=0.3):
70
+ super().__init__()
71
+ self.mlp = nn.Sequential(
72
+ nn.Linear(input_size, hidden_size),
73
+ nn.GELU(),
74
+ nn.BatchNorm1d(hidden_size),
75
+ nn.Dropout(dropout),
76
+ nn.Linear(hidden_size, hidden_size // 2),
77
+ nn.GELU(),
78
+ nn.Dropout(dropout),
79
+ nn.Linear(hidden_size // 2, num_classes),
80
+ )
81
+
82
+ def forward(self, x):
83
+ return self.mlp(x)
84
+
85
+ # 3. Load Models and apply weights
86
+ device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
87
+
88
+ backbone = AutoModel.from_pretrained("facebook/dinov2-with-registers-base").to(device)
89
+ backbone.eval()
90
+ for p in backbone.parameters():
91
+ p.requires_grad_(False)
92
+
93
+ # Load fine-tuned weights into the last 2 blocks of the backbone
94
+ total_layers = len(backbone.encoder.layer)
95
+ unfrozen_state = torch.load(backbone_ckpt, map_location=device, weights_only=True)
96
+ for i, layer in enumerate(backbone.encoder.layer[total_layers - 2:]):
97
+ layer.load_state_dict(unfrozen_state[f"layer.{total_layers - 2 + i}"])
98
+
99
+ head = ScreenDetectorMLP(input_size=3072).to(device)
100
+ head.load_state_dict(torch.load(mlp_ckpt, map_location=device, weights_only=True))
101
+ head.eval()
102
+
103
+ # 4. Prepare Dual-Branch Image Transforms
104
+ IMAGENET_MEAN, IMAGENET_STD = [0.485, 0.456, 0.406], [0.229, 0.224, 0.225]
105
+
106
+ local_transform = transforms.Compose([
107
+ transforms.CenterCrop(224),
108
+ transforms.ToTensor(),
109
+ transforms.Normalize(mean=IMAGENET_MEAN, std=IMAGENET_STD),
110
+ ])
111
+
112
+ global_transform = transforms.Compose([
113
+ transforms.Resize((256, 256)),
114
+ transforms.CenterCrop(224),
115
+ transforms.ToTensor(),
116
+ transforms.Normalize(mean=IMAGENET_MEAN, std=IMAGENET_STD),
117
+ ])
118
+
119
+ # 5. Load Image
120
+ url = "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcS2pnVr5QT5OjVf8H4YtrJfRoPvGBFAG5pBG8F-LfPGB0sEcyF1JkT6Okly&s=10"
121
+ img = Image.open(requests.get(url, stream=True).raw).convert("RGB")
122
+
123
+ local_tensor = local_transform(img).unsqueeze(0).to(device)
124
+ global_tensor = global_transform(img).unsqueeze(0).to(device)
125
+
126
+ # Concatenate for a single forward pass
127
+ batch = torch.cat([local_tensor, global_tensor], dim=0)
128
+
129
+ # 6. Run Inference
130
+ with torch.no_grad():
131
+ out = backbone(pixel_values=batch)
132
+ hidden = out.last_hidden_state.float()
133
+
134
+ # Extract CLS and patch mean (ignoring register tokens)
135
+ n_reg = getattr(backbone.config, "num_register_tokens", 0)
136
+ cls_tok = hidden[:, 0, :]
137
+ patch_mean = hidden[:, 1 + n_reg:, :].mean(dim=1)
138
+ feat = torch.cat([cls_tok, patch_mean], dim=-1)
139
+
140
+ # Split back into local and global, then concatenate horizontally
141
+ local_feat, global_feat = feat[0:1], feat[1:2]
142
+ combined_feat = torch.cat([local_feat, global_feat], dim=-1)
143
+
144
+ # Classify
145
+ logits = head(combined_feat)
146
+ probs = torch.softmax(logits, dim=1)
147
+ conf, pred = probs.max(dim=1)
148
+
149
+ print(f"Prediction: {classes[str(pred.item())]} (Confidence: {conf.item()*100:.1f}%)")
150
+ ```