Upload folder using huggingface_hub
Browse files- LICENSE +21 -0
- README.md +164 -3
- config.json +60 -0
- data/__init__.py +2 -0
- data/dct.py +149 -0
- inference.py +114 -0
- model.json +18 -0
- model.safetensors +3 -0
- models/AIDE.py +297 -0
- models/__init__.py +2 -0
- models/srm_filter_kernel.py +220 -0
- preprocessor_config.json +35 -0
- requirements.txt +7 -0
LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
MIT License
|
| 2 |
+
|
| 3 |
+
Copyright (c) 2024 Shilin Yan
|
| 4 |
+
|
| 5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 6 |
+
of this software and associated documentation files (the "Software"), to deal
|
| 7 |
+
in the Software without restriction, including without limitation the rights
|
| 8 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 9 |
+
copies of the Software, and to permit persons to whom the Software is
|
| 10 |
+
furnished to do so, subject to the following conditions:
|
| 11 |
+
|
| 12 |
+
The above copyright notice and this permission notice shall be included in all
|
| 13 |
+
copies or substantial portions of the Software.
|
| 14 |
+
|
| 15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
| 21 |
+
SOFTWARE.
|
README.md
CHANGED
|
@@ -1,3 +1,164 @@
|
|
| 1 |
-
---
|
| 2 |
-
license:
|
| 3 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
license: mit
|
| 3 |
+
library_name: pytorch
|
| 4 |
+
tags:
|
| 5 |
+
- image-classification
|
| 6 |
+
- ai-generated-image-detection
|
| 7 |
+
- deepfake-detection
|
| 8 |
+
- computer-vision
|
| 9 |
+
- pytorch
|
| 10 |
+
pipeline_tag: image-classification
|
| 11 |
+
---
|
| 12 |
+
|
| 13 |
+
# AIDE Image Detector
|
| 14 |
+
|
| 15 |
+
This repository packages the `checkpoint-19.pth` model from the local AIDE training run at `/home/meet/Aivsre_001/AIDE/output_multisource_run1/checkpoint-19.pth` as a Hugging Face model repository. The exported weights are provided both as a safe deployment artifact in `model.safetensors` and, if uploaded, as the original PyTorch training snapshot `checkpoint-19.pth`.
|
| 16 |
+
|
| 17 |
+
The model is based on **AIDE**: a hybrid AI-generated image detector that combines frequency-forensic evidence and high-level semantic cues. In this run, the detector uses:
|
| 18 |
+
|
| 19 |
+
- A fixed **30-filter SRM high-pass bank** to expose subtle forensic residuals.
|
| 20 |
+
- Two **ResNet-50-style frequency encoders** that process DCT-derived reconstructions.
|
| 21 |
+
- A frozen **OpenCLIP ConvNeXt-XXL visual trunk** for high-level semantic/image-manifold features.
|
| 22 |
+
- A final **MLP fusion head** that merges ConvNeXt and forensic embeddings into a binary classifier: `real` vs `fake`.
|
| 23 |
+
|
| 24 |
+
## Architecture
|
| 25 |
+
|
| 26 |
+
The forward path in [`models/AIDE.py`](./models/AIDE.py) is:
|
| 27 |
+
|
| 28 |
+
1. Start from one RGB image.
|
| 29 |
+
2. Build four DCT-based reconstructed views with [`data/dct.py`](./data/dct.py):
|
| 30 |
+
- `x_minmin`
|
| 31 |
+
- `x_maxmax`
|
| 32 |
+
- `x_minmin1`
|
| 33 |
+
- `x_maxmax1`
|
| 34 |
+
3. Build a fifth view, `x_0`, from the normalized RGB image.
|
| 35 |
+
4. Pass the four DCT views through the fixed SRM high-pass filters and into two ResNet branches.
|
| 36 |
+
5. Pass the RGB view through the frozen OpenCLIP ConvNeXt-XXL trunk.
|
| 37 |
+
6. Project the ConvNeXt pooled embedding from `3072 -> 256`.
|
| 38 |
+
7. Average the four ResNet forensic embeddings into a single `2048`-dimensional frequency representation.
|
| 39 |
+
8. Concatenate `[ConvNeXt_256, Forensic_2048]` into a `2304`-dimensional vector.
|
| 40 |
+
9. Classify with an MLP `2304 -> 1024 -> 2`.
|
| 41 |
+
|
| 42 |
+
Important implementation details taken directly from the code:
|
| 43 |
+
|
| 44 |
+
- The frequency branch uses `HPF -> ResNet(Bottleneck, [3, 4, 6, 3])`.
|
| 45 |
+
- The ConvNeXt branch is constructed with `open_clip.create_model_and_transforms("convnext_xxlarge", pretrained=None)` and then populated from the checkpoint weights.
|
| 46 |
+
- The ConvNeXt trunk is frozen in the model definition used for this checkpoint.
|
| 47 |
+
- Inside the model, the RGB input is remapped from ImageNet normalization to CLIP normalization before entering the ConvNeXt visual trunk.
|
| 48 |
+
|
| 49 |
+
## Input Preparation
|
| 50 |
+
|
| 51 |
+
Inference must follow the same preparation used during training/evaluation:
|
| 52 |
+
|
| 53 |
+
1. Convert the image to RGB.
|
| 54 |
+
2. Convert to tensor in `[0, 1]`.
|
| 55 |
+
3. Use `DCT_base_Rec_Module(window_size=32, stride=16, output=256, grade_N=6)` to reconstruct four frequency-ranked views.
|
| 56 |
+
4. Resize all five views to `256 x 256`.
|
| 57 |
+
5. Normalize each view with:
|
| 58 |
+
|
| 59 |
+
```python
|
| 60 |
+
mean = [0.485, 0.456, 0.406]
|
| 61 |
+
std = [0.229, 0.224, 0.225]
|
| 62 |
+
```
|
| 63 |
+
|
| 64 |
+
6. Stack the views in this exact order:
|
| 65 |
+
|
| 66 |
+
```python
|
| 67 |
+
[x_minmin, x_maxmax, x_minmin1, x_maxmax1, x_0]
|
| 68 |
+
```
|
| 69 |
+
|
| 70 |
+
The provided [`inference.py`](./inference.py) script reproduces this preparation pipeline.
|
| 71 |
+
|
| 72 |
+
## Checkpoint Details
|
| 73 |
+
|
| 74 |
+
- Source checkpoint: `checkpoint-19.pth`
|
| 75 |
+
- Exported safe weights: `model.safetensors`
|
| 76 |
+
- Labels:
|
| 77 |
+
- `0 -> real`
|
| 78 |
+
- `1 -> fake`
|
| 79 |
+
- Epoch: `19`
|
| 80 |
+
- Logged trainable parameters: `54,432,466`
|
| 81 |
+
|
| 82 |
+
From the local training log for `output_multisource_run1`:
|
| 83 |
+
|
| 84 |
+
- Epoch 19 validation/top-1 accuracy: `77.9186`
|
| 85 |
+
- Epoch 19 validation loss: `0.4757`
|
| 86 |
+
- Best validation/top-1 accuracy observed in the same run: `78.5831` at epoch `17`
|
| 87 |
+
|
| 88 |
+
## Training Context
|
| 89 |
+
|
| 90 |
+
This checkpoint came from a multi-source run configured with:
|
| 91 |
+
|
| 92 |
+
- `data_path=/home/meet/Aivsre_001/aide_data/train_multi_v1`
|
| 93 |
+
- `eval_data_path=/home/meet/Aivsre_001/aide_data/eval_multi_v1`
|
| 94 |
+
- `epochs=20`
|
| 95 |
+
- `batch_size=8`
|
| 96 |
+
- `blr=5e-4`
|
| 97 |
+
- `weight_decay=0.0`
|
| 98 |
+
- `nb_classes=2`
|
| 99 |
+
- `aa=rand-m9-mstd0.5-inc1`
|
| 100 |
+
- `smoothing=0.1`
|
| 101 |
+
|
| 102 |
+
The upstream AIDE project is introduced in the paper **"A Sanity Check for AI-generated Image Detection"** and uses a hybrid design intended to improve robustness on challenging real-world AI-image detection settings.
|
| 103 |
+
|
| 104 |
+
## Files In This Repo
|
| 105 |
+
|
| 106 |
+
- `model.safetensors`: exported model state dict for safer deployment.
|
| 107 |
+
- `checkpoint-19.pth`: original PyTorch training snapshot, if uploaded.
|
| 108 |
+
- `config.json`: architecture and label metadata.
|
| 109 |
+
- `model.json`: lightweight manifest for this packaged repo.
|
| 110 |
+
- `preprocessor_config.json`: image normalization and DCT-view preparation metadata.
|
| 111 |
+
- `inference.py`: local loading and prediction helper.
|
| 112 |
+
- `models/` and `data/`: source modules required to reconstruct the architecture.
|
| 113 |
+
|
| 114 |
+
## Usage
|
| 115 |
+
|
| 116 |
+
Clone or download the repository, then install dependencies:
|
| 117 |
+
|
| 118 |
+
```bash
|
| 119 |
+
pip install -r requirements.txt
|
| 120 |
+
```
|
| 121 |
+
|
| 122 |
+
Run local inference:
|
| 123 |
+
|
| 124 |
+
```bash
|
| 125 |
+
python inference.py --repo_dir . --image /path/to/image.jpg
|
| 126 |
+
```
|
| 127 |
+
|
| 128 |
+
Or use it programmatically:
|
| 129 |
+
|
| 130 |
+
```python
|
| 131 |
+
from PIL import Image
|
| 132 |
+
|
| 133 |
+
from inference import load_model, predict_pil_images
|
| 134 |
+
|
| 135 |
+
model = load_model(".")
|
| 136 |
+
image = Image.open("example.jpg").convert("RGB")
|
| 137 |
+
result = predict_pil_images(model, [image])[0]
|
| 138 |
+
print(result)
|
| 139 |
+
```
|
| 140 |
+
|
| 141 |
+
Example output:
|
| 142 |
+
|
| 143 |
+
```python
|
| 144 |
+
{
|
| 145 |
+
"label": "fake",
|
| 146 |
+
"real_probability": 0.082134,
|
| 147 |
+
"fake_probability": 0.917866,
|
| 148 |
+
}
|
| 149 |
+
```
|
| 150 |
+
|
| 151 |
+
## Notes
|
| 152 |
+
|
| 153 |
+
- This repository is designed for **weight hosting and reproducible local inference**.
|
| 154 |
+
- The architecture is custom and is not a native `transformers` `AutoModel` implementation.
|
| 155 |
+
- Because the model relies on OpenCLIP ConvNeXt-XXL plus custom DCT/SRM preprocessing, users should use the provided loader and inference script.
|
| 156 |
+
|
| 157 |
+
## Credits
|
| 158 |
+
|
| 159 |
+
This packaged repository is derived from the original AIDE implementation:
|
| 160 |
+
|
| 161 |
+
- Project: https://github.com/shilinyan99/AIDE
|
| 162 |
+
- Paper: https://arxiv.org/abs/2406.19435
|
| 163 |
+
|
| 164 |
+
Original AIDE code is MIT licensed. See [`LICENSE`](./LICENSE).
|
config.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"architectures": [
|
| 3 |
+
"AIDE"
|
| 4 |
+
],
|
| 5 |
+
"model_type": "aide",
|
| 6 |
+
"library_name": "pytorch",
|
| 7 |
+
"task": "image-classification",
|
| 8 |
+
"num_labels": 2,
|
| 9 |
+
"id2label": {
|
| 10 |
+
"0": "real",
|
| 11 |
+
"1": "fake"
|
| 12 |
+
},
|
| 13 |
+
"label2id": {
|
| 14 |
+
"real": 0,
|
| 15 |
+
"fake": 1
|
| 16 |
+
},
|
| 17 |
+
"image_size": 256,
|
| 18 |
+
"input_tensor_shape": [
|
| 19 |
+
5,
|
| 20 |
+
3,
|
| 21 |
+
256,
|
| 22 |
+
256
|
| 23 |
+
],
|
| 24 |
+
"frequency_branch": {
|
| 25 |
+
"hpf_filters": 30,
|
| 26 |
+
"resnet_backbones": 2,
|
| 27 |
+
"resnet_variant": "ResNet-50-style encoder",
|
| 28 |
+
"output_dim": 2048
|
| 29 |
+
},
|
| 30 |
+
"semantic_branch": {
|
| 31 |
+
"backbone": "OpenCLIP ConvNeXt-XXL visual trunk",
|
| 32 |
+
"pooled_dim": 3072,
|
| 33 |
+
"projection_dim": 256,
|
| 34 |
+
"frozen": true
|
| 35 |
+
},
|
| 36 |
+
"classifier": {
|
| 37 |
+
"type": "mlp",
|
| 38 |
+
"input_dim": 2304,
|
| 39 |
+
"hidden_dim": 1024,
|
| 40 |
+
"output_dim": 2
|
| 41 |
+
},
|
| 42 |
+
"dct_preparation": {
|
| 43 |
+
"window_size": 32,
|
| 44 |
+
"stride": 16,
|
| 45 |
+
"output_size": 256,
|
| 46 |
+
"grade_N": 6,
|
| 47 |
+
"selected_views": [
|
| 48 |
+
"x_minmin",
|
| 49 |
+
"x_maxmax",
|
| 50 |
+
"x_minmin1",
|
| 51 |
+
"x_maxmax1",
|
| 52 |
+
"x_0_rgb"
|
| 53 |
+
]
|
| 54 |
+
},
|
| 55 |
+
"checkpoint": {
|
| 56 |
+
"source": "checkpoint-19.pth",
|
| 57 |
+
"epoch": 19
|
| 58 |
+
}
|
| 59 |
+
}
|
| 60 |
+
|
data/__init__.py
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Package marker for local DCT utilities.
|
| 2 |
+
|
data/dct.py
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import torch
|
| 3 |
+
import torch.nn as nn
|
| 4 |
+
import numpy as np
|
| 5 |
+
import torch.nn.functional as F
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
def DCT_mat(size):
|
| 9 |
+
m = [[ (np.sqrt(1./size) if i == 0 else np.sqrt(2./size)) * np.cos((j + 0.5) * np.pi * i / size) for j in range(size)] for i in range(size)]
|
| 10 |
+
return m
|
| 11 |
+
|
| 12 |
+
def generate_filter(start, end, size):
|
| 13 |
+
return [[0. if i + j > end or i + j < start else 1. for j in range(size)] for i in range(size)]
|
| 14 |
+
|
| 15 |
+
def norm_sigma(x):
|
| 16 |
+
return 2. * torch.sigmoid(x) - 1.
|
| 17 |
+
|
| 18 |
+
class Filter(nn.Module):
|
| 19 |
+
def __init__(self, size, band_start, band_end, use_learnable=False, norm=False):
|
| 20 |
+
super(Filter, self).__init__()
|
| 21 |
+
self.use_learnable = use_learnable
|
| 22 |
+
|
| 23 |
+
self.base = nn.Parameter(torch.tensor(generate_filter(band_start, band_end, size)), requires_grad=False)
|
| 24 |
+
if self.use_learnable:
|
| 25 |
+
self.learnable = nn.Parameter(torch.randn(size, size), requires_grad=True)
|
| 26 |
+
self.learnable.data.normal_(0., 0.1)
|
| 27 |
+
self.norm = norm
|
| 28 |
+
if norm:
|
| 29 |
+
self.ft_num = nn.Parameter(torch.sum(torch.tensor(generate_filter(band_start, band_end, size))), requires_grad=False)
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
def forward(self, x):
|
| 33 |
+
if self.use_learnable:
|
| 34 |
+
filt = self.base + norm_sigma(self.learnable)
|
| 35 |
+
else:
|
| 36 |
+
filt = self.base
|
| 37 |
+
|
| 38 |
+
if self.norm:
|
| 39 |
+
y = x * filt / self.ft_num
|
| 40 |
+
else:
|
| 41 |
+
y = x * filt
|
| 42 |
+
return y
|
| 43 |
+
|
| 44 |
+
class DCT_base_Rec_Module(nn.Module):
|
| 45 |
+
"""_summary_
|
| 46 |
+
|
| 47 |
+
Args:
|
| 48 |
+
x: [C, H, W] -> [C*level, output, output]
|
| 49 |
+
"""
|
| 50 |
+
def __init__(self, window_size=32, stride=16, output=256, grade_N=6, level_fliter=[0]):
|
| 51 |
+
super().__init__()
|
| 52 |
+
|
| 53 |
+
assert output % window_size == 0
|
| 54 |
+
assert len(level_fliter) > 0
|
| 55 |
+
|
| 56 |
+
self.window_size = window_size
|
| 57 |
+
self.grade_N = grade_N
|
| 58 |
+
self.level_N = len(level_fliter)
|
| 59 |
+
self.N = (output // window_size) * (output // window_size)
|
| 60 |
+
|
| 61 |
+
self._DCT_patch = nn.Parameter(torch.tensor(DCT_mat(window_size)).float(), requires_grad=False)
|
| 62 |
+
self._DCT_patch_T = nn.Parameter(torch.transpose(torch.tensor(DCT_mat(window_size)).float(), 0, 1), requires_grad=False)
|
| 63 |
+
|
| 64 |
+
self.unfold = nn.Unfold(
|
| 65 |
+
kernel_size=(window_size, window_size), stride=stride
|
| 66 |
+
)
|
| 67 |
+
self.fold0 = nn.Fold(
|
| 68 |
+
output_size=(window_size, window_size),
|
| 69 |
+
kernel_size=(window_size, window_size),
|
| 70 |
+
stride=window_size
|
| 71 |
+
)
|
| 72 |
+
|
| 73 |
+
lm, mh = 2.82, 2
|
| 74 |
+
level_f = [
|
| 75 |
+
Filter(window_size, 0, window_size * 2)
|
| 76 |
+
]
|
| 77 |
+
|
| 78 |
+
self.level_filters = nn.ModuleList([level_f[i] for i in level_fliter])
|
| 79 |
+
self.grade_filters = nn.ModuleList([Filter(window_size, window_size * 2. / grade_N * i, window_size * 2. / grade_N * (i+1), norm=True) for i in range(grade_N)])
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
def forward(self, x):
|
| 83 |
+
|
| 84 |
+
N = self.N
|
| 85 |
+
grade_N = self.grade_N
|
| 86 |
+
level_N = self.level_N
|
| 87 |
+
window_size = self.window_size
|
| 88 |
+
C, W, H = x.shape
|
| 89 |
+
x_unfold = self.unfold(x.unsqueeze(0)).squeeze(0)
|
| 90 |
+
|
| 91 |
+
|
| 92 |
+
_, L = x_unfold.shape
|
| 93 |
+
x_unfold = x_unfold.transpose(0, 1).reshape(L, C, window_size, window_size)
|
| 94 |
+
x_dct = self._DCT_patch @ x_unfold @ self._DCT_patch_T
|
| 95 |
+
|
| 96 |
+
y_list = []
|
| 97 |
+
for i in range(self.level_N):
|
| 98 |
+
x_pass = self.level_filters[i](x_dct)
|
| 99 |
+
y = self._DCT_patch_T @ x_pass @ self._DCT_patch
|
| 100 |
+
y_list.append(y)
|
| 101 |
+
level_x_unfold = torch.cat(y_list, dim=1)
|
| 102 |
+
|
| 103 |
+
grade = torch.zeros(L).to(x.device)
|
| 104 |
+
w, k = 1, 2
|
| 105 |
+
for _ in range(grade_N):
|
| 106 |
+
_x = torch.abs(x_dct)
|
| 107 |
+
_x = torch.log(_x + 1)
|
| 108 |
+
_x = self.grade_filters[_](_x)
|
| 109 |
+
_x = torch.sum(_x, dim=[1,2,3])
|
| 110 |
+
grade += w * _x
|
| 111 |
+
w *= k
|
| 112 |
+
|
| 113 |
+
_, idx = torch.sort(grade)
|
| 114 |
+
max_idx = torch.flip(idx, dims=[0])[:N]
|
| 115 |
+
maxmax_idx = max_idx[0]
|
| 116 |
+
if len(max_idx) == 1:
|
| 117 |
+
maxmax_idx1 = max_idx[0]
|
| 118 |
+
else:
|
| 119 |
+
maxmax_idx1 = max_idx[1]
|
| 120 |
+
|
| 121 |
+
min_idx = idx[:N]
|
| 122 |
+
minmin_idx = idx[0]
|
| 123 |
+
if len(min_idx) == 1:
|
| 124 |
+
minmin_idx1 = idx[0]
|
| 125 |
+
else:
|
| 126 |
+
minmin_idx1 = idx[1]
|
| 127 |
+
|
| 128 |
+
x_minmin = torch.index_select(level_x_unfold, 0, minmin_idx)
|
| 129 |
+
x_maxmax = torch.index_select(level_x_unfold, 0, maxmax_idx)
|
| 130 |
+
x_minmin1 = torch.index_select(level_x_unfold, 0, minmin_idx1)
|
| 131 |
+
x_maxmax1 = torch.index_select(level_x_unfold, 0, maxmax_idx1)
|
| 132 |
+
|
| 133 |
+
x_minmin = x_minmin.reshape(1, level_N*C*window_size* window_size).transpose(0, 1)
|
| 134 |
+
x_maxmax = x_maxmax.reshape(1, level_N*C*window_size* window_size).transpose(0, 1)
|
| 135 |
+
x_minmin1 = x_minmin1.reshape(1, level_N*C*window_size* window_size).transpose(0, 1)
|
| 136 |
+
x_maxmax1 = x_maxmax1.reshape(1, level_N*C*window_size* window_size).transpose(0, 1)
|
| 137 |
+
|
| 138 |
+
x_minmin = self.fold0(x_minmin)
|
| 139 |
+
x_maxmax = self.fold0(x_maxmax)
|
| 140 |
+
x_minmin1 = self.fold0(x_minmin1)
|
| 141 |
+
x_maxmax1 = self.fold0(x_maxmax1)
|
| 142 |
+
|
| 143 |
+
|
| 144 |
+
return x_minmin, x_maxmax, x_minmin1, x_maxmax1
|
| 145 |
+
|
| 146 |
+
|
| 147 |
+
|
| 148 |
+
|
| 149 |
+
|
inference.py
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import argparse
|
| 4 |
+
from pathlib import Path
|
| 5 |
+
from typing import Iterable, List
|
| 6 |
+
|
| 7 |
+
import numpy as np
|
| 8 |
+
import torch
|
| 9 |
+
from PIL import Image
|
| 10 |
+
from safetensors.torch import load_file as load_safetensors
|
| 11 |
+
from torchvision import transforms
|
| 12 |
+
|
| 13 |
+
from data.dct import DCT_base_Rec_Module
|
| 14 |
+
from models import AIDE as build_aide_model
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
IMAGE_SIZE = 256
|
| 18 |
+
TO_TENSOR = transforms.ToTensor()
|
| 19 |
+
NORMALIZE_AND_RESIZE = transforms.Compose(
|
| 20 |
+
[
|
| 21 |
+
transforms.Resize([IMAGE_SIZE, IMAGE_SIZE]),
|
| 22 |
+
transforms.Normalize(
|
| 23 |
+
mean=[0.485, 0.456, 0.406],
|
| 24 |
+
std=[0.229, 0.224, 0.225],
|
| 25 |
+
),
|
| 26 |
+
]
|
| 27 |
+
)
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
def build_aide_input_from_pil(image: Image.Image, dct_module: DCT_base_Rec_Module) -> torch.Tensor:
|
| 31 |
+
image = image.convert("RGB")
|
| 32 |
+
image_tensor = TO_TENSOR(image)
|
| 33 |
+
x_minmin, x_maxmax, x_minmin1, x_maxmax1 = dct_module(image_tensor)
|
| 34 |
+
|
| 35 |
+
x_0 = NORMALIZE_AND_RESIZE(image_tensor)
|
| 36 |
+
x_minmin = NORMALIZE_AND_RESIZE(x_minmin)
|
| 37 |
+
x_maxmax = NORMALIZE_AND_RESIZE(x_maxmax)
|
| 38 |
+
x_minmin1 = NORMALIZE_AND_RESIZE(x_minmin1)
|
| 39 |
+
x_maxmax1 = NORMALIZE_AND_RESIZE(x_maxmax1)
|
| 40 |
+
|
| 41 |
+
return torch.stack([x_minmin, x_maxmax, x_minmin1, x_maxmax1, x_0], dim=0)
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
def load_model(
|
| 45 |
+
repo_dir: str | Path,
|
| 46 |
+
device: str | None = None,
|
| 47 |
+
weights_name: str = "model.safetensors",
|
| 48 |
+
) -> torch.nn.Module:
|
| 49 |
+
repo_dir = Path(repo_dir)
|
| 50 |
+
weights_path = repo_dir / weights_name
|
| 51 |
+
device = device or ("cuda" if torch.cuda.is_available() else "cpu")
|
| 52 |
+
|
| 53 |
+
model = build_aide_model(resnet_path=None, convnext_path=None)
|
| 54 |
+
state_dict = load_safetensors(str(weights_path))
|
| 55 |
+
model.load_state_dict(state_dict, strict=True)
|
| 56 |
+
model.to(device)
|
| 57 |
+
model.eval()
|
| 58 |
+
return model
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
@torch.inference_mode()
|
| 62 |
+
def predict_pil_images(
|
| 63 |
+
model: torch.nn.Module,
|
| 64 |
+
images: Iterable[Image.Image],
|
| 65 |
+
device: str | None = None,
|
| 66 |
+
) -> List[dict]:
|
| 67 |
+
device = device or next(model.parameters()).device.type
|
| 68 |
+
dct_module = DCT_base_Rec_Module()
|
| 69 |
+
batch = torch.stack([build_aide_input_from_pil(img, dct_module) for img in images], dim=0).to(device)
|
| 70 |
+
logits = model(batch)
|
| 71 |
+
probs = torch.softmax(logits, dim=-1).cpu().numpy()
|
| 72 |
+
|
| 73 |
+
outputs = []
|
| 74 |
+
for prob in probs:
|
| 75 |
+
real_prob = float(prob[0])
|
| 76 |
+
fake_prob = float(prob[1])
|
| 77 |
+
label = "fake" if fake_prob >= real_prob else "real"
|
| 78 |
+
outputs.append(
|
| 79 |
+
{
|
| 80 |
+
"label": label,
|
| 81 |
+
"real_probability": round(real_prob, 6),
|
| 82 |
+
"fake_probability": round(fake_prob, 6),
|
| 83 |
+
}
|
| 84 |
+
)
|
| 85 |
+
return outputs
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
def _load_images(paths: Iterable[str]) -> List[Image.Image]:
|
| 89 |
+
return [Image.open(path).convert("RGB") for path in paths]
|
| 90 |
+
|
| 91 |
+
|
| 92 |
+
def main() -> None:
|
| 93 |
+
parser = argparse.ArgumentParser(description="Run AIDE image detector inference.")
|
| 94 |
+
parser.add_argument("--repo_dir", type=str, default=".", help="Local path to the model repository.")
|
| 95 |
+
parser.add_argument("--image", type=str, nargs="+", required=True, help="One or more image paths.")
|
| 96 |
+
parser.add_argument("--device", type=str, default=None, help="cuda or cpu")
|
| 97 |
+
args = parser.parse_args()
|
| 98 |
+
|
| 99 |
+
model = load_model(args.repo_dir, device=args.device)
|
| 100 |
+
images = _load_images(args.image)
|
| 101 |
+
predictions = predict_pil_images(model, images, device=args.device)
|
| 102 |
+
|
| 103 |
+
for image_path, prediction in zip(args.image, predictions):
|
| 104 |
+
print(
|
| 105 |
+
{
|
| 106 |
+
"image": str(image_path),
|
| 107 |
+
**prediction,
|
| 108 |
+
}
|
| 109 |
+
)
|
| 110 |
+
|
| 111 |
+
|
| 112 |
+
if __name__ == "__main__":
|
| 113 |
+
main()
|
| 114 |
+
|
model.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "AIDE Image Detector",
|
| 3 |
+
"repo_id": "meet4150/AIDE_image_detector",
|
| 4 |
+
"framework": "PyTorch",
|
| 5 |
+
"weights": {
|
| 6 |
+
"primary": "model.safetensors",
|
| 7 |
+
"training_snapshot": "checkpoint-19.pth"
|
| 8 |
+
},
|
| 9 |
+
"config_file": "config.json",
|
| 10 |
+
"preprocessor_file": "preprocessor_config.json",
|
| 11 |
+
"inference_file": "inference.py",
|
| 12 |
+
"labels": [
|
| 13 |
+
"real",
|
| 14 |
+
"fake"
|
| 15 |
+
],
|
| 16 |
+
"notes": "Hybrid AIDE checkpoint exported from /home/meet/Aivsre_001/AIDE/output_multisource_run1/checkpoint-19.pth."
|
| 17 |
+
}
|
| 18 |
+
|
model.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:f9c39868bbefde44af859ff760a9763b719562087e8d019fd78407db1520e177
|
| 3 |
+
size 3591869768
|
models/AIDE.py
ADDED
|
@@ -0,0 +1,297 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
import torch.nn as nn
|
| 3 |
+
import open_clip
|
| 4 |
+
import numpy as np
|
| 5 |
+
|
| 6 |
+
from .srm_filter_kernel import all_normalized_hpf_list
|
| 7 |
+
|
| 8 |
+
class HPF(nn.Module):
|
| 9 |
+
def __init__(self):
|
| 10 |
+
super(HPF, self).__init__()
|
| 11 |
+
|
| 12 |
+
#Load 30 SRM Filters
|
| 13 |
+
all_hpf_list_5x5 = []
|
| 14 |
+
|
| 15 |
+
for hpf_item in all_normalized_hpf_list:
|
| 16 |
+
if hpf_item.shape[0] == 3:
|
| 17 |
+
hpf_item = np.pad(hpf_item, pad_width=((1, 1), (1, 1)), mode='constant')
|
| 18 |
+
|
| 19 |
+
all_hpf_list_5x5.append(hpf_item)
|
| 20 |
+
|
| 21 |
+
hpf_weight = torch.Tensor(all_hpf_list_5x5).view(30, 1, 5, 5).contiguous()
|
| 22 |
+
hpf_weight = torch.nn.Parameter(hpf_weight.repeat(1, 3, 1, 1), requires_grad=False)
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
self.hpf = nn.Conv2d(3, 30, kernel_size=5, padding=2, bias=False)
|
| 26 |
+
self.hpf.weight = hpf_weight
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def forward(self, input):
|
| 30 |
+
|
| 31 |
+
output = self.hpf(input)
|
| 32 |
+
|
| 33 |
+
return output
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
def conv3x3(in_planes, out_planes, stride=1):
|
| 38 |
+
"""3x3 convolution with padding"""
|
| 39 |
+
return nn.Conv2d(in_planes, out_planes, kernel_size=3, stride=stride,
|
| 40 |
+
padding=1, bias=False)
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
def conv1x1(in_planes, out_planes, stride=1):
|
| 44 |
+
"""1x1 convolution"""
|
| 45 |
+
return nn.Conv2d(in_planes, out_planes, kernel_size=1, stride=stride, bias=False)
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
class BasicBlock(nn.Module):
|
| 49 |
+
expansion = 1
|
| 50 |
+
|
| 51 |
+
def __init__(self, inplanes, planes, stride=1, downsample=None):
|
| 52 |
+
super(BasicBlock, self).__init__()
|
| 53 |
+
self.conv1 = conv3x3(inplanes, planes, stride)
|
| 54 |
+
self.bn1 = nn.BatchNorm2d(planes)
|
| 55 |
+
self.relu = nn.ReLU(inplace=True)
|
| 56 |
+
self.conv2 = conv3x3(planes, planes)
|
| 57 |
+
self.bn2 = nn.BatchNorm2d(planes)
|
| 58 |
+
self.downsample = downsample
|
| 59 |
+
self.stride = stride
|
| 60 |
+
|
| 61 |
+
def forward(self, x):
|
| 62 |
+
identity = x
|
| 63 |
+
|
| 64 |
+
out = self.conv1(x)
|
| 65 |
+
out = self.bn1(out)
|
| 66 |
+
out = self.relu(out)
|
| 67 |
+
|
| 68 |
+
out = self.conv2(out)
|
| 69 |
+
out = self.bn2(out)
|
| 70 |
+
|
| 71 |
+
if self.downsample is not None:
|
| 72 |
+
identity = self.downsample(x)
|
| 73 |
+
|
| 74 |
+
out += identity
|
| 75 |
+
out = self.relu(out)
|
| 76 |
+
|
| 77 |
+
return out
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
class Bottleneck(nn.Module):
|
| 81 |
+
expansion = 4
|
| 82 |
+
|
| 83 |
+
def __init__(self, inplanes, planes, stride=1, downsample=None):
|
| 84 |
+
super(Bottleneck, self).__init__()
|
| 85 |
+
self.conv1 = conv1x1(inplanes, planes)
|
| 86 |
+
self.bn1 = nn.BatchNorm2d(planes)
|
| 87 |
+
self.conv2 = conv3x3(planes, planes, stride)
|
| 88 |
+
self.bn2 = nn.BatchNorm2d(planes)
|
| 89 |
+
self.conv3 = conv1x1(planes, planes * self.expansion)
|
| 90 |
+
self.bn3 = nn.BatchNorm2d(planes * self.expansion)
|
| 91 |
+
self.relu = nn.ReLU(inplace=True)
|
| 92 |
+
self.downsample = downsample
|
| 93 |
+
self.stride = stride
|
| 94 |
+
|
| 95 |
+
def forward(self, x):
|
| 96 |
+
identity = x
|
| 97 |
+
|
| 98 |
+
out = self.conv1(x)
|
| 99 |
+
out = self.bn1(out)
|
| 100 |
+
out = self.relu(out)
|
| 101 |
+
|
| 102 |
+
out = self.conv2(out)
|
| 103 |
+
out = self.bn2(out)
|
| 104 |
+
out = self.relu(out)
|
| 105 |
+
|
| 106 |
+
out = self.conv3(out)
|
| 107 |
+
out = self.bn3(out)
|
| 108 |
+
|
| 109 |
+
if self.downsample is not None:
|
| 110 |
+
identity = self.downsample(x)
|
| 111 |
+
|
| 112 |
+
out += identity
|
| 113 |
+
out = self.relu(out)
|
| 114 |
+
|
| 115 |
+
return out
|
| 116 |
+
|
| 117 |
+
|
| 118 |
+
class ResNet(nn.Module):
|
| 119 |
+
|
| 120 |
+
def __init__(self, block, layers, num_classes=1000, zero_init_residual=True):
|
| 121 |
+
super(ResNet, self).__init__()
|
| 122 |
+
|
| 123 |
+
self.inplanes = 64
|
| 124 |
+
self.conv1 = nn.Conv2d(30, 64, kernel_size=7, stride=2, padding=3,
|
| 125 |
+
bias=False)
|
| 126 |
+
self.bn1 = nn.BatchNorm2d(64)
|
| 127 |
+
self.relu = nn.ReLU(inplace=True)
|
| 128 |
+
self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
|
| 129 |
+
self.layer1 = self._make_layer(block, 64, layers[0])
|
| 130 |
+
self.layer2 = self._make_layer(block, 128, layers[1], stride=2)
|
| 131 |
+
self.layer3 = self._make_layer(block, 256, layers[2], stride=2)
|
| 132 |
+
self.layer4 = self._make_layer(block, 512, layers[3], stride=2)
|
| 133 |
+
self.avgpool = nn.AdaptiveAvgPool2d((1, 1))
|
| 134 |
+
self.fc = nn.Linear(512 * block.expansion, num_classes)
|
| 135 |
+
|
| 136 |
+
for m in self.modules():
|
| 137 |
+
if isinstance(m, nn.Conv2d):
|
| 138 |
+
nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')
|
| 139 |
+
elif isinstance(m, nn.BatchNorm2d):
|
| 140 |
+
nn.init.constant_(m.weight, 1)
|
| 141 |
+
nn.init.constant_(m.bias, 0)
|
| 142 |
+
|
| 143 |
+
# Zero-initialize the last BN in each residual branch,
|
| 144 |
+
# so that the residual branch starts with zeros, and each residual block behaves like an identity.
|
| 145 |
+
# This improves the model by 0.2~0.3% according to https://arxiv.org/abs/1706.02677
|
| 146 |
+
if zero_init_residual:
|
| 147 |
+
for m in self.modules():
|
| 148 |
+
if isinstance(m, Bottleneck):
|
| 149 |
+
nn.init.constant_(m.bn3.weight, 0)
|
| 150 |
+
elif isinstance(m, BasicBlock):
|
| 151 |
+
nn.init.constant_(m.bn2.weight, 0)
|
| 152 |
+
|
| 153 |
+
def _make_layer(self, block, planes, blocks, stride=1):
|
| 154 |
+
downsample = None
|
| 155 |
+
if stride != 1 or self.inplanes != planes * block.expansion:
|
| 156 |
+
downsample = nn.Sequential(
|
| 157 |
+
conv1x1(self.inplanes, planes * block.expansion, stride),
|
| 158 |
+
nn.BatchNorm2d(planes * block.expansion),
|
| 159 |
+
)
|
| 160 |
+
|
| 161 |
+
layers = []
|
| 162 |
+
layers.append(block(self.inplanes, planes, stride, downsample))
|
| 163 |
+
self.inplanes = planes * block.expansion
|
| 164 |
+
for _ in range(1, blocks):
|
| 165 |
+
layers.append(block(self.inplanes, planes))
|
| 166 |
+
|
| 167 |
+
return nn.Sequential(*layers)
|
| 168 |
+
|
| 169 |
+
def forward(self, x):
|
| 170 |
+
|
| 171 |
+
x = self.conv1(x)
|
| 172 |
+
x = self.bn1(x)
|
| 173 |
+
x = self.relu(x)
|
| 174 |
+
x = self.maxpool(x)
|
| 175 |
+
|
| 176 |
+
x = self.layer1(x)
|
| 177 |
+
x = self.layer2(x)
|
| 178 |
+
x = self.layer3(x)
|
| 179 |
+
x = self.layer4(x)
|
| 180 |
+
|
| 181 |
+
x = self.avgpool(x)
|
| 182 |
+
x = x.view(x.size(0), -1)
|
| 183 |
+
|
| 184 |
+
|
| 185 |
+
return x
|
| 186 |
+
|
| 187 |
+
class Mlp(nn.Module):
|
| 188 |
+
""" MLP as used in Vision Transformer, MLP-Mixer and related networks
|
| 189 |
+
"""
|
| 190 |
+
|
| 191 |
+
def __init__(self, in_features, hidden_features=None, out_features=None, act_layer=nn.GELU):
|
| 192 |
+
super().__init__()
|
| 193 |
+
out_features = out_features or in_features
|
| 194 |
+
hidden_features = hidden_features or in_features
|
| 195 |
+
|
| 196 |
+
self.fc1 = nn.Linear(in_features, hidden_features)
|
| 197 |
+
self.act = act_layer()
|
| 198 |
+
self.fc2 = nn.Linear(hidden_features, out_features)
|
| 199 |
+
|
| 200 |
+
def forward(self, x):
|
| 201 |
+
x = self.fc1(x)
|
| 202 |
+
x = self.act(x)
|
| 203 |
+
x = self.fc2(x)
|
| 204 |
+
return x
|
| 205 |
+
|
| 206 |
+
class AIDE_Model(nn.Module):
|
| 207 |
+
|
| 208 |
+
def __init__(self, resnet_path, convnext_path):
|
| 209 |
+
super(AIDE_Model, self).__init__()
|
| 210 |
+
self.hpf = HPF()
|
| 211 |
+
self.model_min = ResNet(Bottleneck, [3, 4, 6, 3])
|
| 212 |
+
self.model_max = ResNet(Bottleneck, [3, 4, 6, 3])
|
| 213 |
+
|
| 214 |
+
if resnet_path is not None:
|
| 215 |
+
pretrained_dict = torch.load(resnet_path, map_location='cpu')
|
| 216 |
+
|
| 217 |
+
model_min_dict = self.model_min.state_dict()
|
| 218 |
+
model_max_dict = self.model_max.state_dict()
|
| 219 |
+
|
| 220 |
+
for k in pretrained_dict.keys():
|
| 221 |
+
if k in model_min_dict and pretrained_dict[k].size() == model_min_dict[k].size():
|
| 222 |
+
model_min_dict[k] = pretrained_dict[k]
|
| 223 |
+
model_max_dict[k] = pretrained_dict[k]
|
| 224 |
+
else:
|
| 225 |
+
print(f"Skipping layer {k} because of size mismatch")
|
| 226 |
+
|
| 227 |
+
self.fc = Mlp(2048 + 256 , 1024, 2)
|
| 228 |
+
|
| 229 |
+
print("build model with convnext_xxl")
|
| 230 |
+
self.openclip_convnext_xxl, _, _ = open_clip.create_model_and_transforms(
|
| 231 |
+
"convnext_xxlarge", pretrained=convnext_path
|
| 232 |
+
)
|
| 233 |
+
|
| 234 |
+
self.openclip_convnext_xxl = self.openclip_convnext_xxl.visual.trunk
|
| 235 |
+
self.openclip_convnext_xxl.head.global_pool = nn.Identity()
|
| 236 |
+
self.openclip_convnext_xxl.head.flatten = nn.Identity()
|
| 237 |
+
|
| 238 |
+
self.openclip_convnext_xxl.eval()
|
| 239 |
+
|
| 240 |
+
self.avgpool = nn.AdaptiveAvgPool2d((1, 1))
|
| 241 |
+
self.convnext_proj = nn.Sequential(
|
| 242 |
+
nn.Linear(3072, 256),
|
| 243 |
+
|
| 244 |
+
)
|
| 245 |
+
for param in self.openclip_convnext_xxl.parameters():
|
| 246 |
+
param.requires_grad = False
|
| 247 |
+
|
| 248 |
+
|
| 249 |
+
|
| 250 |
+
def forward(self, x):
|
| 251 |
+
|
| 252 |
+
b, t, c, h, w = x.shape
|
| 253 |
+
|
| 254 |
+
x_minmin = x[:, 0] #[b, c, h, w]
|
| 255 |
+
x_maxmax = x[:, 1]
|
| 256 |
+
x_minmin1 = x[:, 2]
|
| 257 |
+
x_maxmax1 = x[:, 3]
|
| 258 |
+
tokens = x[:, 4]
|
| 259 |
+
|
| 260 |
+
x_minmin = self.hpf(x_minmin)
|
| 261 |
+
x_maxmax = self.hpf(x_maxmax)
|
| 262 |
+
x_minmin1 = self.hpf(x_minmin1)
|
| 263 |
+
x_maxmax1 = self.hpf(x_maxmax1)
|
| 264 |
+
|
| 265 |
+
with torch.no_grad():
|
| 266 |
+
|
| 267 |
+
clip_mean = torch.Tensor([0.48145466, 0.4578275, 0.40821073])
|
| 268 |
+
clip_mean = clip_mean.to(tokens, non_blocking=True).view(3, 1, 1)
|
| 269 |
+
clip_std = torch.Tensor([0.26862954, 0.26130258, 0.27577711])
|
| 270 |
+
clip_std = clip_std.to(tokens, non_blocking=True).view(3, 1, 1)
|
| 271 |
+
dinov2_mean = torch.Tensor([0.485, 0.456, 0.406]).to(tokens, non_blocking=True).view(3, 1, 1)
|
| 272 |
+
dinov2_std = torch.Tensor([0.229, 0.224, 0.225]).to(tokens, non_blocking=True).view(3, 1, 1)
|
| 273 |
+
|
| 274 |
+
local_convnext_image_feats = self.openclip_convnext_xxl(
|
| 275 |
+
tokens * (dinov2_std / clip_std) + (dinov2_mean - clip_mean) / clip_std
|
| 276 |
+
) #[b, 3072, 8, 8]
|
| 277 |
+
assert local_convnext_image_feats.size()[1:] == (3072, 8, 8)
|
| 278 |
+
local_convnext_image_feats = self.avgpool(local_convnext_image_feats).view(tokens.size(0), -1)
|
| 279 |
+
x_0 = self.convnext_proj(local_convnext_image_feats)
|
| 280 |
+
|
| 281 |
+
x_min = self.model_min(x_minmin)
|
| 282 |
+
x_max = self.model_max(x_maxmax)
|
| 283 |
+
x_min1 = self.model_min(x_minmin1)
|
| 284 |
+
x_max1 = self.model_max(x_maxmax1)
|
| 285 |
+
|
| 286 |
+
x_1 = (x_min + x_max + x_min1 + x_max1) / 4
|
| 287 |
+
|
| 288 |
+
x = torch.cat([x_0, x_1], dim=1)
|
| 289 |
+
|
| 290 |
+
x = self.fc(x)
|
| 291 |
+
|
| 292 |
+
return x
|
| 293 |
+
|
| 294 |
+
def AIDE(resnet_path, convnext_path):
|
| 295 |
+
model = AIDE_Model(resnet_path, convnext_path)
|
| 296 |
+
return model
|
| 297 |
+
|
models/__init__.py
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .AIDE import AIDE
|
| 2 |
+
|
models/srm_filter_kernel.py
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
import numpy as np
|
| 3 |
+
|
| 4 |
+
filter_class_1 = [
|
| 5 |
+
np.array([
|
| 6 |
+
[1, 0, 0],
|
| 7 |
+
[0, -1, 0],
|
| 8 |
+
[0, 0, 0]
|
| 9 |
+
], dtype=np.float32),
|
| 10 |
+
np.array([
|
| 11 |
+
[0, 1, 0],
|
| 12 |
+
[0, -1, 0],
|
| 13 |
+
[0, 0, 0]
|
| 14 |
+
], dtype=np.float32),
|
| 15 |
+
np.array([
|
| 16 |
+
[0, 0, 1],
|
| 17 |
+
[0, -1, 0],
|
| 18 |
+
[0, 0, 0]
|
| 19 |
+
], dtype=np.float32),
|
| 20 |
+
np.array([
|
| 21 |
+
[0, 0, 0],
|
| 22 |
+
[1, -1, 0],
|
| 23 |
+
[0, 0, 0]
|
| 24 |
+
], dtype=np.float32),
|
| 25 |
+
np.array([
|
| 26 |
+
[0, 0, 0],
|
| 27 |
+
[0, -1, 1],
|
| 28 |
+
[0, 0, 0]
|
| 29 |
+
], dtype=np.float32),
|
| 30 |
+
np.array([
|
| 31 |
+
[0, 0, 0],
|
| 32 |
+
[0, -1, 0],
|
| 33 |
+
[1, 0, 0]
|
| 34 |
+
], dtype=np.float32),
|
| 35 |
+
np.array([
|
| 36 |
+
[0, 0, 0],
|
| 37 |
+
[0, -1, 0],
|
| 38 |
+
[0, 1, 0]
|
| 39 |
+
], dtype=np.float32),
|
| 40 |
+
np.array([
|
| 41 |
+
[0, 0, 0],
|
| 42 |
+
[0, -1, 0],
|
| 43 |
+
[0, 0, 1]
|
| 44 |
+
], dtype=np.float32)
|
| 45 |
+
]
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
filter_class_2 = [
|
| 49 |
+
np.array([
|
| 50 |
+
[1, 0, 0],
|
| 51 |
+
[0, -2, 0],
|
| 52 |
+
[0, 0, 1]
|
| 53 |
+
], dtype=np.float32),
|
| 54 |
+
np.array([
|
| 55 |
+
[0, 1, 0],
|
| 56 |
+
[0, -2, 0],
|
| 57 |
+
[0, 1, 0]
|
| 58 |
+
], dtype=np.float32),
|
| 59 |
+
np.array([
|
| 60 |
+
[0, 0, 1],
|
| 61 |
+
[0, -2, 0],
|
| 62 |
+
[1, 0, 0]
|
| 63 |
+
], dtype=np.float32),
|
| 64 |
+
np.array([
|
| 65 |
+
[0, 0, 0],
|
| 66 |
+
[1, -2, 1],
|
| 67 |
+
[0, 0, 0]
|
| 68 |
+
], dtype=np.float32),
|
| 69 |
+
]
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
filter_class_3 = [
|
| 73 |
+
np.array([
|
| 74 |
+
[-1, 0, 0, 0, 0],
|
| 75 |
+
[0, 3, 0, 0, 0],
|
| 76 |
+
[0, 0, -3, 0, 0],
|
| 77 |
+
[0, 0, 0, 1, 0],
|
| 78 |
+
[0, 0, 0, 0, 0]
|
| 79 |
+
], dtype=np.float32),
|
| 80 |
+
np.array([
|
| 81 |
+
[0, 0, -1, 0, 0],
|
| 82 |
+
[0, 0, 3, 0, 0],
|
| 83 |
+
[0, 0, -3, 0, 0],
|
| 84 |
+
[0, 0, 1, 0, 0],
|
| 85 |
+
[0, 0, 0, 0, 0]
|
| 86 |
+
], dtype=np.float32),
|
| 87 |
+
np.array([
|
| 88 |
+
[0, 0, 0, 0, -1],
|
| 89 |
+
[0, 0, 0, 3, 0],
|
| 90 |
+
[0, 0, -3, 0, 0],
|
| 91 |
+
[0, 1, 0, 0, 0],
|
| 92 |
+
[0, 0, 0, 0, 0]
|
| 93 |
+
], dtype=np.float32),
|
| 94 |
+
np.array([
|
| 95 |
+
[0, 0, 0, 0, 0],
|
| 96 |
+
[0, 0, 0, 0, 0],
|
| 97 |
+
[0, 1, -3, 3, -1],
|
| 98 |
+
[0, 0, 0, 0, 0],
|
| 99 |
+
[0, 0, 0, 0, 0]
|
| 100 |
+
], dtype=np.float32),
|
| 101 |
+
np.array([
|
| 102 |
+
[0, 0, 0, 0, 0],
|
| 103 |
+
[0, 1, 0, 0, 0],
|
| 104 |
+
[0, 0, -3, 0, 0],
|
| 105 |
+
[0, 0, 0, 3, 0],
|
| 106 |
+
[0, 0, 0, 0, -1]
|
| 107 |
+
], dtype=np.float32),
|
| 108 |
+
np.array([
|
| 109 |
+
[0, 0, 0, 0, 0],
|
| 110 |
+
[0, 0, 1, 0, 0],
|
| 111 |
+
[0, 0, -3, 0, 0],
|
| 112 |
+
[0, 0, 3, 0, 0],
|
| 113 |
+
[0, 0, -1, 0, 0]
|
| 114 |
+
], dtype=np.float32),
|
| 115 |
+
np.array([
|
| 116 |
+
[0, 0, 0, 0, 0],
|
| 117 |
+
[0, 0, 0, 1, 0],
|
| 118 |
+
[0, 0, -3, 0, 0],
|
| 119 |
+
[0, 3, 0, 0, 0],
|
| 120 |
+
[-1, 0, 0, 0, 0]
|
| 121 |
+
], dtype=np.float32),
|
| 122 |
+
np.array([
|
| 123 |
+
[0, 0, 0, 0, 0],
|
| 124 |
+
[0, 0, 0, 0, 0],
|
| 125 |
+
[-1, 3, -3, 1, 0],
|
| 126 |
+
[0, 0, 0, 0, 0],
|
| 127 |
+
[0, 0, 0, 0, 0]
|
| 128 |
+
], dtype=np.float32)
|
| 129 |
+
]
|
| 130 |
+
|
| 131 |
+
|
| 132 |
+
filter_edge_3x3 = [
|
| 133 |
+
np.array([
|
| 134 |
+
[-1, 2, -1],
|
| 135 |
+
[2, -4, 2],
|
| 136 |
+
[0, 0, 0]
|
| 137 |
+
], dtype=np.float32),
|
| 138 |
+
np.array([
|
| 139 |
+
[0, 2, -1],
|
| 140 |
+
[0, -4, 2],
|
| 141 |
+
[0, 2, -1]
|
| 142 |
+
], dtype=np.float32),
|
| 143 |
+
np.array([
|
| 144 |
+
[0, 0, 0],
|
| 145 |
+
[2, -4, 2],
|
| 146 |
+
[-1, 2, -1]
|
| 147 |
+
], dtype=np.float32),
|
| 148 |
+
np.array([
|
| 149 |
+
[-1, 2, 0],
|
| 150 |
+
[2, -4, 0],
|
| 151 |
+
[-1, 2, 0]
|
| 152 |
+
], dtype=np.float32),
|
| 153 |
+
]
|
| 154 |
+
|
| 155 |
+
filter_edge_5x5 = [
|
| 156 |
+
np.array([
|
| 157 |
+
[-1, 2, -2, 2, -1],
|
| 158 |
+
[2, -6, 8, -6, 2],
|
| 159 |
+
[-2, 8, -12, 8, -2],
|
| 160 |
+
[0, 0, 0, 0, 0],
|
| 161 |
+
[0, 0, 0, 0, 0]
|
| 162 |
+
], dtype=np.float32),
|
| 163 |
+
np.array([
|
| 164 |
+
[0, 0, -2, 2, -1],
|
| 165 |
+
[0, 0, 8, -6, 2],
|
| 166 |
+
[0, 0, -12, 8, -2],
|
| 167 |
+
[0, 0, 8, -6, 2],
|
| 168 |
+
[0, 0, -2, 2, -1]
|
| 169 |
+
], dtype=np.float32),
|
| 170 |
+
np.array([
|
| 171 |
+
[0, 0, 0, 0, 0],
|
| 172 |
+
[0, 0, 0, 0, 0],
|
| 173 |
+
[-2, 8, -12, 8, -2],
|
| 174 |
+
[2, -6, 8, -6, 2],
|
| 175 |
+
[-1, 2, -2, 2, -1]
|
| 176 |
+
], dtype=np.float32),
|
| 177 |
+
np.array([
|
| 178 |
+
[-1, 2, -2, 0, 0],
|
| 179 |
+
[2, -6, 8, 0, 0],
|
| 180 |
+
[-2, 8, -12, 0, 0],
|
| 181 |
+
[2, -6, 8, 0, 0],
|
| 182 |
+
[-1, 2, -2, 0, 0]
|
| 183 |
+
], dtype=np.float32),
|
| 184 |
+
]
|
| 185 |
+
|
| 186 |
+
square_3x3 = np.array([
|
| 187 |
+
[-1, 2, -1],
|
| 188 |
+
[2, -4, 2],
|
| 189 |
+
[-1, 2, -1]
|
| 190 |
+
], dtype=np.float32)
|
| 191 |
+
|
| 192 |
+
square_5x5 = np.array([
|
| 193 |
+
[-1, 2, -2, 2, -1],
|
| 194 |
+
[2, -6, 8, -6, 2],
|
| 195 |
+
[-2, 8, -12, 8, -2],
|
| 196 |
+
[2, -6, 8, -6, 2],
|
| 197 |
+
[-1, 2, -2, 2, -1]
|
| 198 |
+
], dtype=np.float32)
|
| 199 |
+
|
| 200 |
+
|
| 201 |
+
all_hpf_list = filter_class_1 + filter_class_2 + filter_class_3 + filter_edge_3x3 + filter_edge_5x5 + [square_3x3, square_5x5]
|
| 202 |
+
|
| 203 |
+
hpf_3x3_list = filter_class_1 + filter_class_2 + filter_edge_3x3 + [square_3x3]
|
| 204 |
+
hpf_5x5_list = filter_class_3 + filter_edge_5x5 + [square_5x5]
|
| 205 |
+
|
| 206 |
+
normalized_filter_class_2 = [hpf / 2 for hpf in filter_class_2]
|
| 207 |
+
normalized_filter_class_3 = [hpf / 3 for hpf in filter_class_3]
|
| 208 |
+
normalized_filter_edge_3x3 = [hpf / 4 for hpf in filter_edge_3x3]
|
| 209 |
+
normalized_square_3x3 = square_3x3 / 4
|
| 210 |
+
normalized_filter_edge_5x5 = [hpf / 12 for hpf in filter_edge_5x5]
|
| 211 |
+
normalized_square_5x5 = square_5x5 / 12
|
| 212 |
+
|
| 213 |
+
all_normalized_hpf_list = filter_class_1 + normalized_filter_class_2 + normalized_filter_class_3 + \
|
| 214 |
+
normalized_filter_edge_3x3 + normalized_filter_edge_5x5 + [normalized_square_3x3, normalized_square_5x5]
|
| 215 |
+
|
| 216 |
+
normalized_hpf_3x3_list = filter_class_1 + normalized_filter_class_2 + normalized_filter_edge_3x3 + [normalized_square_3x3]
|
| 217 |
+
normalized_hpf_5x5_list = normalized_filter_class_3 + normalized_filter_edge_5x5 + [normalized_square_5x5]
|
| 218 |
+
|
| 219 |
+
normalized_3x3_list = normalized_filter_edge_3x3 + [normalized_square_3x3]
|
| 220 |
+
normalized_5x5_list = normalized_filter_edge_5x5 + [normalized_square_5x5]
|
preprocessor_config.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"do_convert_rgb": true,
|
| 3 |
+
"do_resize": true,
|
| 4 |
+
"size": {
|
| 5 |
+
"height": 256,
|
| 6 |
+
"width": 256
|
| 7 |
+
},
|
| 8 |
+
"resample": 3,
|
| 9 |
+
"do_rescale": true,
|
| 10 |
+
"rescale_factor": 0.00392156862745098,
|
| 11 |
+
"do_normalize": true,
|
| 12 |
+
"image_mean": [
|
| 13 |
+
0.485,
|
| 14 |
+
0.456,
|
| 15 |
+
0.406
|
| 16 |
+
],
|
| 17 |
+
"image_std": [
|
| 18 |
+
0.229,
|
| 19 |
+
0.224,
|
| 20 |
+
0.225
|
| 21 |
+
],
|
| 22 |
+
"extra_views": {
|
| 23 |
+
"type": "dct_reconstruction",
|
| 24 |
+
"window_size": 32,
|
| 25 |
+
"stride": 16,
|
| 26 |
+
"grade_N": 6,
|
| 27 |
+
"selected_views": [
|
| 28 |
+
"x_minmin",
|
| 29 |
+
"x_maxmax",
|
| 30 |
+
"x_minmin1",
|
| 31 |
+
"x_maxmax1"
|
| 32 |
+
]
|
| 33 |
+
}
|
| 34 |
+
}
|
| 35 |
+
|
requirements.txt
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
torch>=2.0
|
| 2 |
+
torchvision>=0.15
|
| 3 |
+
numpy>=1.24
|
| 4 |
+
Pillow>=9.5
|
| 5 |
+
safetensors>=0.4
|
| 6 |
+
open-clip-torch>=2.24.0
|
| 7 |
+
|