MAS-AI-0000 commited on
Commit
0bbd37f
·
verified ·
1 Parent(s): 13464bf

Update imagePreprocess.py

Browse files
Files changed (1) hide show
  1. imagePreprocess.py +223 -223
imagePreprocess.py CHANGED
@@ -1,223 +1,223 @@
1
- import os
2
- from pathlib import Path
3
- from PIL import Image, ImageOps
4
- import cv2
5
- import numpy as np
6
- import tensorflow as tf
7
- from tensorflow.keras.applications.resnet50 import preprocess_input
8
- import torch
9
- import clip
10
-
11
- BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
12
- MODELS_DIR = os.path.join(BASE_DIR, "Lib/Models/Image")
13
-
14
- # Load models and preprocessing once at module level
15
- clip_mod, clip_pre = clip.load("ViT-B/32", jit=False)
16
- clip_mod.eval()
17
- for p in clip_mod.parameters():
18
- p.requires_grad = False
19
- mlp_model= tf.keras.models.load_model(os.path.join(MODELS_DIR, "clip_model.keras"))
20
- cnn_model = tf.keras.models.load_model(os.path.join(MODELS_DIR, "cnn_model.keras"))
21
- resnet_model = tf.keras.models.load_model(os.path.join(MODELS_DIR, "resnet_model.keras"))
22
-
23
-
24
- def center_crop(image: Image.Image, crop_size=512) -> Image.Image | str:
25
- try:
26
- image = ImageOps.exif_transpose(image)
27
- w, h = image.size
28
- if w < crop_size or h < crop_size:
29
- # skip small images
30
- return f"skipped image (too small) ({w}x{h})"
31
- left = (w - crop_size) // 2
32
- top = (h - crop_size) // 2
33
- right = left + crop_size
34
- bottom = top + crop_size
35
- cropped = image.crop((left, top, right, bottom))
36
- return cropped
37
- except Exception as e:
38
- return f"Error when cropping center: {e}"
39
-
40
-
41
- def denoise(src_image: Image) -> np.ndarray | str:
42
- """Read image, denoise (GPU if available) and return denoised image."""
43
- img = np.array(src_image) # BGR uint8 numpy array
44
- img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
45
- if src_image is None:
46
- print(f"WARNING: No source image, skipping.")
47
- return False
48
- # Denoising parameters
49
- H = 10 # filter strength for luminance component (recommended 3-15)
50
- H_COLOR = 10 # same for color components
51
- TEMPLATE_WINDOW_SIZE = 7
52
- SEARCH_WINDOW_SIZE = 21
53
- # Use CUDA if available, otherwise CPU fallback
54
- use_cuda = False
55
- try:
56
- use_cuda = hasattr(cv2, 'cuda') and cv2.cuda.getCudaEnabledDeviceCount() > 0
57
- except Exception:
58
- use_cuda = False
59
- if use_cuda:
60
- # Create a GpuMat and upload the numpy image to GPU
61
- gpu_img = cv2.cuda_GpuMat()
62
- gpu_img.upload(img) # <-- this converts numpy -> GpuMat on device
63
- den_gpu = cv2.cuda.fastNlMeansDenoisingColored(
64
- gpu_img,H,H_COLOR,None,SEARCH_WINDOW_SIZE,TEMPLATE_WINDOW_SIZE
65
- )
66
-
67
- # Download result back to CPU
68
- den = den_gpu.download()
69
- else:
70
- # Fallback to CPU implementation
71
- print("NOTICE: CUDA not available — using CPU denoiser.")
72
- den = cv2.fastNlMeansDenoisingColored(
73
- img, None,
74
- H, H_COLOR,
75
- TEMPLATE_WINDOW_SIZE,
76
- SEARCH_WINDOW_SIZE
77
- )
78
- #cv2.imwrite("denoised.png", den) # for debugging
79
- den = cv2.cvtColor(den, cv2.COLOR_BGR2RGB)
80
- den = Image.fromarray(den)
81
- return den
82
-
83
- def compute_profile(raw_image: Image, den_image: Image, normalize=False ,verbose= True) -> np.ndarray | str:
84
- # read images
85
- if raw_image is None:
86
- return print(f"WARNING: couldn't read raw image")
87
- if den_image is None:
88
- return print(f"WARNING: couldn't read denoised image")
89
-
90
- raw = np.array(raw_image) # RGB uint8 numpy array
91
- raw = cv2.cvtColor(raw, cv2.COLOR_RGB2BGR)
92
- den = np.array(den_image) # RGB uint8 numpy array
93
- den = cv2.cvtColor(den, cv2.COLOR_RGB2BGR)
94
- # if shapes differ, resize den to raw's size (keeps alignment); warn
95
- if den.shape != raw.shape:
96
- if verbose:
97
- print(f"NOTE: shape mismatch, resizing denoised from {den.shape[:2]} to {raw.shape[:2]}")
98
- den = cv2.resize(den, (raw.shape[1], raw.shape[0]), interpolation=cv2.INTER_LINEAR)
99
-
100
- # absolute difference per-channel
101
- diff = cv2.absdiff(raw, den) # BGR, uint8
102
- gray = cv2.cvtColor(diff, cv2.COLOR_BGR2GRAY) # single-channel uint8
103
-
104
- # optionally normalize to full 0-255 (per-image)
105
- if normalize:
106
- # cv2.normalize will map min->0 and max->255
107
- # but if the image is flat (min==max) normalize will set to 0; handle that
108
- minv = int(gray.min())
109
- maxv = int(gray.max())
110
- if maxv > minv:
111
- norm = cv2.normalize(gray, None, alpha=0, beta=255, norm_type=cv2.NORM_MINMAX)
112
- out = norm
113
- else:
114
- # nothing to normalize (flat), keep as-is (all zeros)
115
- out = gray
116
- else:
117
- # keep raw diff values but ensure dtype uint8 (already uint8) and values are 0..255
118
- out = gray
119
- #cv2.imwrite("profile.png", out) # for debugging
120
- return out
121
-
122
- def profile_image_for_cnn_predict(pil_img: Image, crop_size=512):
123
- """Preprocess the input image and return a numpy array ready for model prediction."""
124
- # Step 1: Center crop the image
125
- cropped_img = center_crop(pil_img, crop_size=crop_size)
126
- if isinstance(cropped_img, str):
127
- return cropped_img # return error message if cropping failed
128
- # Step 2: Denoise the cropped image
129
- denoised_img = denoise(cropped_img)
130
- if isinstance(denoised_img, str):
131
- return denoised_img # return error message if denoising failed
132
- # Step 3: Compute the profile image
133
- profile_img = compute_profile(cropped_img, denoised_img, normalize=False)
134
- if isinstance(profile_img, str):
135
- return profile_img # return error message if profile computation failed
136
- return profile_img
137
-
138
-
139
- def prepare_cv2_image_for_resnet(cv2_gray_img, target_size=(512,512)):
140
- img_rgb = cv2.cvtColor(cv2_gray_img, cv2.COLOR_GRAY2RGB)
141
- img_rgb = cv2.resize(img_rgb, (target_size[1], target_size[0]), interpolation=cv2.INTER_AREA)
142
- img_rgb = img_rgb.astype('float32')
143
- # 5) add batch dim
144
- x = np.expand_dims(img_rgb, axis=0) # shape (1, H, W, 3)
145
- x = preprocess_input(x)
146
- return x
147
-
148
- def predict_image_prob_clip(image: Image.Image, threshold=0.5,
149
- clip_model=None, clip_preprocess=None,
150
- keras_mlp=None):
151
- """
152
- Predicts probability that image is AI-generated (AI=1) using CLIP + Keras MLP.
153
-
154
- Args:
155
- path_or_image: str (file path) or PIL.Image.Image or numpy array (H,W,3)
156
- threshold: float threshold for binary label
157
- clip_model, clip_preprocess: optionally pass existing CLIP objects
158
- keras_mlp: optionally pass existing loaded Keras model
159
- Returns:
160
- dict: {'prob': float_prob_AI, 'label': 'AI' or 'Real'}
161
- """
162
-
163
-
164
- # --- try to reuse provided CLIP objects, otherwise load ---
165
- if clip_model is None or clip_preprocess is None:
166
- print("Loading Default CLIP model...")
167
- # pick a model name: prefer provided arg, else try global, else ViT-B/32
168
- cmn = "ViT-B/32"
169
- clip_model, clip_preprocess = clip.load(cmn, device="cpu", jit=False)
170
- clip_model.eval()
171
- for p in clip_model.parameters():
172
- p.requires_grad = False
173
-
174
- # --- try to reuse provided keras model, otherwise load from disk ---
175
- if keras_mlp is None:
176
- print("No keras model provided...")
177
- return None
178
- # --- load/normalize image ---
179
- # assume PIL image
180
- img = image.convert('RGB')
181
-
182
- # --- preprocess for CLIP and get embedding ---
183
- input_tensor = clip_preprocess(img).unsqueeze(0).to("cpu") # shape (1,C,H,W)
184
- with torch.no_grad():
185
- emb = clip_model.encode_image(input_tensor) # (1, D)
186
- emb = emb / emb.norm(dim=-1, keepdim=True) # L2 normalize
187
-
188
- emb_np = emb.cpu().numpy().astype('float32') # shape (1, D)
189
-
190
- # --- predict with Keras MLP ---
191
- probs = keras_mlp.predict(emb_np, verbose=0).reshape(-1,)
192
- prob = float(probs[0])
193
- return prob
194
-
195
- def clip_predict(pil_img: Image, crop_size=512):
196
- # pass model objects explicitly (faster if you call this repeatedly)
197
- pil_img = center_crop(pil_img, crop_size=crop_size)
198
-
199
- if isinstance(pil_img, str):
200
- return pil_img # return error message
201
-
202
- return predict_image_prob_clip(pil_img,
203
- clip_model=clip_mod,
204
- clip_preprocess=clip_pre,
205
- keras_mlp=mlp_model)
206
-
207
-
208
- def CNNPredict(predict_img: np.ndarray):
209
- #1 Real 0 AI
210
- #normalize image
211
- # expand dims to add channel axis
212
- predict_img = predict_img.astype('float32') / 255.0 # shape (H, W)
213
- predict_img = np.expand_dims(predict_img, axis=-1) # shape (H, W, 1)
214
- # expand dims to add batch axis
215
- predict_img = np.expand_dims(predict_img, axis=0) # shape (1, H, W, 1)
216
- prediction = cnn_model.predict(predict_img)
217
- return prediction[0][0]
218
-
219
- def ResnetPredict(predict_img):
220
- #1 Real 0 AI
221
- predict_img = prepare_cv2_image_for_resnet(predict_img)
222
- prediction = resnet_model.predict(predict_img)
223
- return prediction[0][0]
 
1
+ import os
2
+ from pathlib import Path
3
+ from PIL import Image, ImageOps
4
+ import cv2
5
+ import numpy as np
6
+ import tensorflow as tf
7
+ from tensorflow.keras.applications.resnet50 import preprocess_input
8
+ import torch
9
+ import clip
10
+
11
+ BASE_DIR = "MAS-AI-0000/Authentica"
12
+ MODELS_DIR = os.path.join(BASE_DIR, "Lib/Models/Image")
13
+
14
+ # Load models and preprocessing once at module level
15
+ clip_mod, clip_pre = clip.load("ViT-B/32", jit=False)
16
+ clip_mod.eval()
17
+ for p in clip_mod.parameters():
18
+ p.requires_grad = False
19
+ mlp_model= tf.keras.models.load_model(os.path.join(MODELS_DIR, "clip_model.keras"))
20
+ cnn_model = tf.keras.models.load_model(os.path.join(MODELS_DIR, "cnn_model.keras"))
21
+ resnet_model = tf.keras.models.load_model(os.path.join(MODELS_DIR, "resnet_model.keras"))
22
+
23
+
24
+ def center_crop(image: Image.Image, crop_size=512) -> Image.Image | str:
25
+ try:
26
+ image = ImageOps.exif_transpose(image)
27
+ w, h = image.size
28
+ if w < crop_size or h < crop_size:
29
+ # skip small images
30
+ return f"skipped image (too small) ({w}x{h})"
31
+ left = (w - crop_size) // 2
32
+ top = (h - crop_size) // 2
33
+ right = left + crop_size
34
+ bottom = top + crop_size
35
+ cropped = image.crop((left, top, right, bottom))
36
+ return cropped
37
+ except Exception as e:
38
+ return f"Error when cropping center: {e}"
39
+
40
+
41
+ def denoise(src_image: Image) -> np.ndarray | str:
42
+ """Read image, denoise (GPU if available) and return denoised image."""
43
+ img = np.array(src_image) # BGR uint8 numpy array
44
+ img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
45
+ if src_image is None:
46
+ print(f"WARNING: No source image, skipping.")
47
+ return False
48
+ # Denoising parameters
49
+ H = 10 # filter strength for luminance component (recommended 3-15)
50
+ H_COLOR = 10 # same for color components
51
+ TEMPLATE_WINDOW_SIZE = 7
52
+ SEARCH_WINDOW_SIZE = 21
53
+ # Use CUDA if available, otherwise CPU fallback
54
+ use_cuda = False
55
+ try:
56
+ use_cuda = hasattr(cv2, 'cuda') and cv2.cuda.getCudaEnabledDeviceCount() > 0
57
+ except Exception:
58
+ use_cuda = False
59
+ if use_cuda:
60
+ # Create a GpuMat and upload the numpy image to GPU
61
+ gpu_img = cv2.cuda_GpuMat()
62
+ gpu_img.upload(img) # <-- this converts numpy -> GpuMat on device
63
+ den_gpu = cv2.cuda.fastNlMeansDenoisingColored(
64
+ gpu_img,H,H_COLOR,None,SEARCH_WINDOW_SIZE,TEMPLATE_WINDOW_SIZE
65
+ )
66
+
67
+ # Download result back to CPU
68
+ den = den_gpu.download()
69
+ else:
70
+ # Fallback to CPU implementation
71
+ print("NOTICE: CUDA not available — using CPU denoiser.")
72
+ den = cv2.fastNlMeansDenoisingColored(
73
+ img, None,
74
+ H, H_COLOR,
75
+ TEMPLATE_WINDOW_SIZE,
76
+ SEARCH_WINDOW_SIZE
77
+ )
78
+ #cv2.imwrite("denoised.png", den) # for debugging
79
+ den = cv2.cvtColor(den, cv2.COLOR_BGR2RGB)
80
+ den = Image.fromarray(den)
81
+ return den
82
+
83
+ def compute_profile(raw_image: Image, den_image: Image, normalize=False ,verbose= True) -> np.ndarray | str:
84
+ # read images
85
+ if raw_image is None:
86
+ return print(f"WARNING: couldn't read raw image")
87
+ if den_image is None:
88
+ return print(f"WARNING: couldn't read denoised image")
89
+
90
+ raw = np.array(raw_image) # RGB uint8 numpy array
91
+ raw = cv2.cvtColor(raw, cv2.COLOR_RGB2BGR)
92
+ den = np.array(den_image) # RGB uint8 numpy array
93
+ den = cv2.cvtColor(den, cv2.COLOR_RGB2BGR)
94
+ # if shapes differ, resize den to raw's size (keeps alignment); warn
95
+ if den.shape != raw.shape:
96
+ if verbose:
97
+ print(f"NOTE: shape mismatch, resizing denoised from {den.shape[:2]} to {raw.shape[:2]}")
98
+ den = cv2.resize(den, (raw.shape[1], raw.shape[0]), interpolation=cv2.INTER_LINEAR)
99
+
100
+ # absolute difference per-channel
101
+ diff = cv2.absdiff(raw, den) # BGR, uint8
102
+ gray = cv2.cvtColor(diff, cv2.COLOR_BGR2GRAY) # single-channel uint8
103
+
104
+ # optionally normalize to full 0-255 (per-image)
105
+ if normalize:
106
+ # cv2.normalize will map min->0 and max->255
107
+ # but if the image is flat (min==max) normalize will set to 0; handle that
108
+ minv = int(gray.min())
109
+ maxv = int(gray.max())
110
+ if maxv > minv:
111
+ norm = cv2.normalize(gray, None, alpha=0, beta=255, norm_type=cv2.NORM_MINMAX)
112
+ out = norm
113
+ else:
114
+ # nothing to normalize (flat), keep as-is (all zeros)
115
+ out = gray
116
+ else:
117
+ # keep raw diff values but ensure dtype uint8 (already uint8) and values are 0..255
118
+ out = gray
119
+ #cv2.imwrite("profile.png", out) # for debugging
120
+ return out
121
+
122
+ def profile_image_for_cnn_predict(pil_img: Image, crop_size=512):
123
+ """Preprocess the input image and return a numpy array ready for model prediction."""
124
+ # Step 1: Center crop the image
125
+ cropped_img = center_crop(pil_img, crop_size=crop_size)
126
+ if isinstance(cropped_img, str):
127
+ return cropped_img # return error message if cropping failed
128
+ # Step 2: Denoise the cropped image
129
+ denoised_img = denoise(cropped_img)
130
+ if isinstance(denoised_img, str):
131
+ return denoised_img # return error message if denoising failed
132
+ # Step 3: Compute the profile image
133
+ profile_img = compute_profile(cropped_img, denoised_img, normalize=False)
134
+ if isinstance(profile_img, str):
135
+ return profile_img # return error message if profile computation failed
136
+ return profile_img
137
+
138
+
139
+ def prepare_cv2_image_for_resnet(cv2_gray_img, target_size=(512,512)):
140
+ img_rgb = cv2.cvtColor(cv2_gray_img, cv2.COLOR_GRAY2RGB)
141
+ img_rgb = cv2.resize(img_rgb, (target_size[1], target_size[0]), interpolation=cv2.INTER_AREA)
142
+ img_rgb = img_rgb.astype('float32')
143
+ # 5) add batch dim
144
+ x = np.expand_dims(img_rgb, axis=0) # shape (1, H, W, 3)
145
+ x = preprocess_input(x)
146
+ return x
147
+
148
+ def predict_image_prob_clip(image: Image.Image, threshold=0.5,
149
+ clip_model=None, clip_preprocess=None,
150
+ keras_mlp=None):
151
+ """
152
+ Predicts probability that image is AI-generated (AI=1) using CLIP + Keras MLP.
153
+
154
+ Args:
155
+ path_or_image: str (file path) or PIL.Image.Image or numpy array (H,W,3)
156
+ threshold: float threshold for binary label
157
+ clip_model, clip_preprocess: optionally pass existing CLIP objects
158
+ keras_mlp: optionally pass existing loaded Keras model
159
+ Returns:
160
+ dict: {'prob': float_prob_AI, 'label': 'AI' or 'Real'}
161
+ """
162
+
163
+
164
+ # --- try to reuse provided CLIP objects, otherwise load ---
165
+ if clip_model is None or clip_preprocess is None:
166
+ print("Loading Default CLIP model...")
167
+ # pick a model name: prefer provided arg, else try global, else ViT-B/32
168
+ cmn = "ViT-B/32"
169
+ clip_model, clip_preprocess = clip.load(cmn, device="cpu", jit=False)
170
+ clip_model.eval()
171
+ for p in clip_model.parameters():
172
+ p.requires_grad = False
173
+
174
+ # --- try to reuse provided keras model, otherwise load from disk ---
175
+ if keras_mlp is None:
176
+ print("No keras model provided...")
177
+ return None
178
+ # --- load/normalize image ---
179
+ # assume PIL image
180
+ img = image.convert('RGB')
181
+
182
+ # --- preprocess for CLIP and get embedding ---
183
+ input_tensor = clip_preprocess(img).unsqueeze(0).to("cpu") # shape (1,C,H,W)
184
+ with torch.no_grad():
185
+ emb = clip_model.encode_image(input_tensor) # (1, D)
186
+ emb = emb / emb.norm(dim=-1, keepdim=True) # L2 normalize
187
+
188
+ emb_np = emb.cpu().numpy().astype('float32') # shape (1, D)
189
+
190
+ # --- predict with Keras MLP ---
191
+ probs = keras_mlp.predict(emb_np, verbose=0).reshape(-1,)
192
+ prob = float(probs[0])
193
+ return prob
194
+
195
+ def clip_predict(pil_img: Image, crop_size=512):
196
+ # pass model objects explicitly (faster if you call this repeatedly)
197
+ pil_img = center_crop(pil_img, crop_size=crop_size)
198
+
199
+ if isinstance(pil_img, str):
200
+ return pil_img # return error message
201
+
202
+ return predict_image_prob_clip(pil_img,
203
+ clip_model=clip_mod,
204
+ clip_preprocess=clip_pre,
205
+ keras_mlp=mlp_model)
206
+
207
+
208
+ def CNNPredict(predict_img: np.ndarray):
209
+ #1 Real 0 AI
210
+ #normalize image
211
+ # expand dims to add channel axis
212
+ predict_img = predict_img.astype('float32') / 255.0 # shape (H, W)
213
+ predict_img = np.expand_dims(predict_img, axis=-1) # shape (H, W, 1)
214
+ # expand dims to add batch axis
215
+ predict_img = np.expand_dims(predict_img, axis=0) # shape (1, H, W, 1)
216
+ prediction = cnn_model.predict(predict_img)
217
+ return prediction[0][0]
218
+
219
+ def ResnetPredict(predict_img):
220
+ #1 Real 0 AI
221
+ predict_img = prepare_cv2_image_for_resnet(predict_img)
222
+ prediction = resnet_model.predict(predict_img)
223
+ return prediction[0][0]