MuskAI commited on
Commit
a2a2e29
·
verified ·
1 Parent(s): 0d8aa44

Use non-overlapping full blocks with boundary-only completion

Browse files
README.md CHANGED
@@ -51,7 +51,7 @@ do not represent eight entirely held-out generators.
51
 
52
  ## Download and run
53
 
54
- Use **Python 3.11**. Replace `MuskAI/LTF-v3.3` with this repository's ID.
55
 
56
  ```bash
57
  python -m pip install -U huggingface_hub
@@ -81,8 +81,8 @@ print(result["prediction"], result["score_fake"])
81
  print(result["probabilities"])
82
  ```
83
 
84
- The image is decoded as RGB. All original edge-aligned 224×224 local tiles plus
85
- a global view use the original CLIP preprocessing. Input files are not
86
  recompressed. Test tiling depends on image size. Labels are **0=real, 1=fake**.
87
  The API returns L1/L2/fusion scores, the selected label and the view count.
88
  Loading the model does not download external weights after this repository
@@ -163,3 +163,12 @@ Please cite the LTF paper:
163
  archivePrefix={arXiv}
164
  }
165
  ```
 
 
 
 
 
 
 
 
 
 
51
 
52
  ## Download and run
53
 
54
+ Use **Python 3.11**. The commands below use this model repository.
55
 
56
  ```bash
57
  python -m pip install -U huggingface_hub
 
81
  print(result["probabilities"])
82
  ```
83
 
84
+ The image is decoded as RGB. Non-overlapping full 224×224 local blocks, with right/bottom boundary completion,
85
+ plus a global view use CLIP preprocessing. Input files are not
86
  recompressed. Test tiling depends on image size. Labels are **0=real, 1=fake**.
87
  The API returns L1/L2/fusion scores, the selected label and the view count.
88
  Loading the model does not download external weights after this repository
 
163
  archivePrefix={arXiv}
164
  }
165
  ```
166
+
167
+ ## Tiling policy
168
+
169
+ Full 224×224 blocks are taken without overlap. If pixels remain at the right
170
+ or bottom, a final block is aligned to that boundary; only boundary blocks
171
+ may overlap. For width 500, horizontal starts are 0, 224, 276. The global view
172
+ is separate. This corrects earlier uniformly overlapping tiling; weights are
173
+ unchanged, but predictions can change. Historical benchmark results have not
174
+ been recomputed with this preprocessing.
demo/app.py CHANGED
@@ -495,7 +495,7 @@ def build_demo():
495
  elem_classes="attention-image",
496
  )
497
  gr.Markdown(
498
- "Tiles are numbered row by row. Edge alignment can produce overlap. Some labels are omitted on dense grids; complete coordinates are listed below."
499
  )
500
  with gr.Tab("Tile · Patch Details", id="patch"):
501
  gr.Markdown(
 
495
  elem_classes="attention-image",
496
  )
497
  gr.Markdown(
498
+ "Tiles are numbered row by row. Full 224-pixel blocks do not overlap; only the final right/bottom boundary blocks may overlap. Some labels are omitted on dense grids; complete coordinates are listed below."
499
  )
500
  with gr.Tab("Tile · Patch Details", id="patch"):
501
  gr.Markdown(
demo/attention.py CHANGED
@@ -64,7 +64,7 @@ def capture_prediction(detector, path, branch, threshold):
64
 
65
 
66
  def tile_boxes(width, height, tile_size=224):
67
- # Use the exact same edge-aligned coordinates and row-major order as inference.
68
  from utils.tile_dataset_srm import get_start_positions
69
 
70
  return [
@@ -256,6 +256,7 @@ def parameter_rows(detector, branch, threshold, views):
256
  ],
257
  ["Inference", "CLIP chunk size", str(detector.model.eval_chunk_size)],
258
  ["Inference", "Views: global + local", f"1 + {views - 1} = {views}"],
 
259
  ["Architecture", "Backbone", "Frozen CLIP ViT-L/14"],
260
  [
261
  "Architecture",
 
64
 
65
 
66
  def tile_boxes(width, height, tile_size=224):
67
+ # Match inference: non-overlapping full blocks, then right/bottom boundary blocks.
68
  from utils.tile_dataset_srm import get_start_positions
69
 
70
  return [
 
256
  ],
257
  ["Inference", "CLIP chunk size", str(detector.model.eval_chunk_size)],
258
  ["Inference", "Views: global + local", f"1 + {views - 1} = {views}"],
259
+ ["Inference", "Tiling policy", "Non-overlapping full blocks; overlap only at right/bottom boundaries"],
260
  ["Architecture", "Backbone", "Frozen CLIP ViT-L/14"],
261
  [
262
  "Architecture",
ltf_final/detector.py CHANGED
@@ -1,4 +1,4 @@
1
- """Offline model loading and the original experiment's image preprocessing."""
2
  from __future__ import annotations
3
 
4
  import contextlib
@@ -78,7 +78,7 @@ def create_model(config, weights=None, device='cpu'):
78
 
79
 
80
  def prepare_image(path, tile_size=224):
81
- """Same RGB, tiling, Resize/CenterCrop and CLIP normalization as original eval."""
82
  path = Path(path).expanduser().resolve()
83
  with Image.open(path) as source:
84
  image = source.convert('RGB')
@@ -122,4 +122,5 @@ class LTFDetector:
122
  label = int(score >= threshold)
123
  return dict(path=str(path), prediction='fake' if label else 'real', label=label,
124
  branch=branch, threshold=float(threshold), score_fake=score,
 
125
  views=int(images.shape[0]), probabilities=probabilities)
 
1
+ """Offline model loading and non-overlapping tiling with boundary completion."""
2
  from __future__ import annotations
3
 
4
  import contextlib
 
78
 
79
 
80
  def prepare_image(path, tile_size=224):
81
+ """Original RGB and CLIP normalization; non-overlapping blocks with boundary completion."""
82
  path = Path(path).expanduser().resolve()
83
  with Image.open(path) as source:
84
  image = source.convert('RGB')
 
122
  label = int(score >= threshold)
123
  return dict(path=str(path), prediction='fake' if label else 'real', label=label,
124
  branch=branch, threshold=float(threshold), score_fake=score,
125
+ tiling_policy="nonoverlap_then_boundary",
126
  views=int(images.shape[0]), probabilities=probabilities)
release_manifest.json CHANGED
@@ -11,7 +11,8 @@
11
  "Tensor-only checkpoint loading",
12
  "Portable evaluation configuration",
13
  "Preserve Hub snapshot root when source files are symlinks",
14
- "English source comments and docstrings; executable AST unchanged"
 
15
  ],
16
  "files": {
17
  "ltf_final/__init__.py": {
@@ -19,8 +20,8 @@
19
  "bytes": 175
20
  },
21
  "ltf_final/detector.py": {
22
- "sha256": "998a82c15b253b52905c02201d79b7d92fff30fa95a95c97d48772206d44fb27",
23
- "bytes": 6074
24
  },
25
  "model/LTF.py": {
26
  "sha256": "60d12ab01bf5d748ab0d0d453d99e18512d1a5043c742a2030108d9f15cb7f92",
@@ -63,8 +64,8 @@
63
  "bytes": 0
64
  },
65
  "utils/tile_dataset_srm.py": {
66
- "sha256": "49106f01b4fda4d10e7db8364967988a68a00c39daa2dde8fe89f52506531642",
67
- "bytes": 14623
68
  },
69
  "configs/eval.yaml": {
70
  "sha256": "4ce443731e70a4ac4eb71fb56103f33373e179fefe766c3e6a02635efdb94efc",
@@ -78,5 +79,6 @@
78
  "sha256": "b8cca3fd41ae0c99ba7e8951adf17d267cdb84cd88be6f7c2e0eca1737a03836",
79
  "bytes": 932768134
80
  }
81
- }
 
82
  }
 
11
  "Tensor-only checkpoint loading",
12
  "Portable evaluation configuration",
13
  "Preserve Hub snapshot root when source files are symlinks",
14
+ "English source comments and docstrings; executable AST unchanged",
15
+ "Non-overlapping full 224-pixel blocks with right/bottom boundary completion; historical benchmark numbers not recomputed"
16
  ],
17
  "files": {
18
  "ltf_final/__init__.py": {
 
20
  "bytes": 175
21
  },
22
  "ltf_final/detector.py": {
23
+ "sha256": "fee36f9976efea2de60f0d106f9a3693ebc394cfa5dd4277470bd618f0612307",
24
+ "bytes": 6147
25
  },
26
  "model/LTF.py": {
27
  "sha256": "60d12ab01bf5d748ab0d0d453d99e18512d1a5043c742a2030108d9f15cb7f92",
 
64
  "bytes": 0
65
  },
66
  "utils/tile_dataset_srm.py": {
67
+ "sha256": "a79a5edc0804aa0331805ba27276d09fa4928b3f2938a08eb3fa398682824c84",
68
+ "bytes": 14576
69
  },
70
  "configs/eval.yaml": {
71
  "sha256": "4ce443731e70a4ac4eb71fb56103f33373e179fefe766c3e6a02635efdb94efc",
 
79
  "sha256": "b8cca3fd41ae0c99ba7e8951adf17d267cdb84cd88be6f7c2e0eca1737a03836",
80
  "bytes": 932768134
81
  }
82
+ },
83
+ "tiling_policy": "nonoverlap_then_boundary"
84
  }
utils/tile_dataset_srm.py CHANGED
@@ -52,15 +52,13 @@ def pad_crop_image(image, target_width, target_height):
52
  return image
53
 
54
  def get_start_positions(image_length, block_size, bias=0):
 
55
  if image_length <= block_size:
56
- return [0+bias]
57
- num_blocks = image_length // block_size + (image_length % block_size > 0)
58
- if num_blocks == 1:
59
- return [0+bias]
60
- total_stride = (image_length - block_size) / (num_blocks - 1)
61
- positions = [int(round(i * total_stride) + bias) for i in range(num_blocks - 1)]
62
- positions.append(image_length - block_size + bias)
63
- return positions
64
 
65
  def crop_image_to_tiles(image, block_size):
66
  """
 
52
  return image
53
 
54
  def get_start_positions(image_length, block_size, bias=0):
55
+ """Non-overlapping full blocks, plus one boundary block when needed."""
56
  if image_length <= block_size:
57
+ return [bias]
58
+ positions = list(range(0, image_length - block_size + 1, block_size))
59
+ if positions[-1] != image_length - block_size:
60
+ positions.append(image_length - block_size)
61
+ return [position + bias for position in positions]
 
 
 
62
 
63
  def crop_image_to_tiles(image, block_size):
64
  """