Upload 8 files
Browse files- .gitignore +40 -0
- README.md +87 -0
- ai_detector.py +482 -0
- app.py +119 -0
- example.py +67 -0
- model-card.md +113 -0
- pytorch_model.py +101 -0
- requirements.txt +8 -0
.gitignore
ADDED
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Python
|
2 |
+
__pycache__/
|
3 |
+
*.py[cod]
|
4 |
+
*$py.class
|
5 |
+
*.so
|
6 |
+
.Python
|
7 |
+
env/
|
8 |
+
build/
|
9 |
+
develop-eggs/
|
10 |
+
dist/
|
11 |
+
downloads/
|
12 |
+
eggs/
|
13 |
+
.eggs/
|
14 |
+
lib/
|
15 |
+
lib64/
|
16 |
+
parts/
|
17 |
+
sdist/
|
18 |
+
var/
|
19 |
+
*.egg-info/
|
20 |
+
.installed.cfg
|
21 |
+
*.egg
|
22 |
+
|
23 |
+
# Temporary files
|
24 |
+
*.tmp
|
25 |
+
temp_ela.jpg
|
26 |
+
*.log
|
27 |
+
|
28 |
+
# Virtual Environment
|
29 |
+
venv/
|
30 |
+
ENV/
|
31 |
+
|
32 |
+
# IDE files
|
33 |
+
.idea/
|
34 |
+
.vscode/
|
35 |
+
*.swp
|
36 |
+
*.swo
|
37 |
+
|
38 |
+
# OS specific
|
39 |
+
.DS_Store
|
40 |
+
Thumbs.db
|
README.md
ADDED
@@ -0,0 +1,87 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Enhanced AI Image Detector
|
2 |
+
|
3 |
+
This model detects whether an image is real or AI-generated using a trained PyTorch neural network.
|
4 |
+
|
5 |
+
## Model Description
|
6 |
+
|
7 |
+
The Enhanced AI Image Detector uses a trained PyTorch neural network to analyze images and determine whether they are authentic photographs or generated by AI tools like DALL-E, Midjourney, or Stable Diffusion.
|
8 |
+
|
9 |
+
### Key Features
|
10 |
+
|
11 |
+
- **Deep Learning Model**: Uses a convolutional neural network trained on thousands of real and AI-generated images
|
12 |
+
- **High Accuracy**: Achieves over 85% accuracy in detecting AI-generated content
|
13 |
+
- **Fast Inference**: Optimized for quick analysis even on CPU-only systems
|
14 |
+
- **Simple API**: Easy to use with a straightforward Python interface
|
15 |
+
|
16 |
+
## How It Works
|
17 |
+
|
18 |
+
The model uses a deep convolutional neural network trained on a large dataset of real and AI-generated images. The network learns to detect subtle patterns and artifacts that are characteristic of AI-generated content, including:
|
19 |
+
|
20 |
+
1. **Noise and Artifact Patterns**:
|
21 |
+
- Specific noise patterns introduced by AI generation methods
|
22 |
+
- Artifacts and inconsistencies in image details
|
23 |
+
|
24 |
+
2. **Texture Inconsistencies**:
|
25 |
+
- Unnatural texture patterns
|
26 |
+
- Texture smoothness and regularity
|
27 |
+
|
28 |
+
3. **Color and Lighting Anomalies**:
|
29 |
+
- Unusual color distributions
|
30 |
+
- Lighting inconsistencies
|
31 |
+
|
32 |
+
4. **Structural Patterns**:
|
33 |
+
- Geometric inconsistencies
|
34 |
+
- Unnatural object boundaries
|
35 |
+
- Perspective and proportion issues
|
36 |
+
|
37 |
+
## Usage
|
38 |
+
|
39 |
+
```python
|
40 |
+
from ai_detector import EnhancedAIDetector
|
41 |
+
|
42 |
+
# Initialize the detector with the path to the model file
|
43 |
+
detector = EnhancedAIDetector(model_path='best_model_improved.pth')
|
44 |
+
|
45 |
+
# Analyze an image
|
46 |
+
result = detector.analyze_image("path/to/image.jpg")
|
47 |
+
|
48 |
+
# Check the result
|
49 |
+
if result["is_ai_generated"]:
|
50 |
+
print("This image is likely AI-generated")
|
51 |
+
print(f"Confidence score: {result['overall_score']:.2f}")
|
52 |
+
else:
|
53 |
+
print("This image is likely authentic")
|
54 |
+
print(f"Confidence score: {1 - result['overall_score']:.2f}")
|
55 |
+
|
56 |
+
# View model information
|
57 |
+
print(f"Model: {result.get('model_name', 'Enhanced AI Image Detector')}")
|
58 |
+
print(f"Version: {result.get('model_version', '1.0.0')}")
|
59 |
+
```
|
60 |
+
|
61 |
+
## Requirements
|
62 |
+
|
63 |
+
- PyTorch
|
64 |
+
- TorchVision
|
65 |
+
- OpenCV (cv2)
|
66 |
+
- NumPy
|
67 |
+
- PIL (Pillow)
|
68 |
+
|
69 |
+
## Limitations
|
70 |
+
|
71 |
+
- The model may struggle with highly realistic AI-generated images from newer generation models
|
72 |
+
- Some real images with unusual characteristics may be misclassified
|
73 |
+
- Performance depends on image quality and resolution
|
74 |
+
- The model works best with images similar to those in its training dataset
|
75 |
+
|
76 |
+
## Citation
|
77 |
+
|
78 |
+
If you use this model in your research or application, please cite:
|
79 |
+
|
80 |
+
```
|
81 |
+
@software{enhanced_ai_detector,
|
82 |
+
author = {Your Name},
|
83 |
+
title = {Enhanced AI Image Detector},
|
84 |
+
year = {2025},
|
85 |
+
url = {https://huggingface.co/yourusername/enhanced-ai-detector}
|
86 |
+
}
|
87 |
+
```
|
ai_detector.py
ADDED
@@ -0,0 +1,482 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""
|
2 |
+
Enhanced AI Image Detector - Hugging Face Version
|
3 |
+
This model detects whether an image is real or AI-generated using a trained PyTorch model.
|
4 |
+
"""
|
5 |
+
|
6 |
+
import os
|
7 |
+
import cv2
|
8 |
+
import numpy as np
|
9 |
+
from PIL import Image
|
10 |
+
import json
|
11 |
+
from pytorch_model import PyTorchAIDetector
|
12 |
+
|
13 |
+
class EnhancedAIDetector:
|
14 |
+
"""
|
15 |
+
Enhanced detector for AI-generated images using a trained PyTorch model.
|
16 |
+
"""
|
17 |
+
|
18 |
+
def __init__(self, model_path='best_model_improved.pth'):
|
19 |
+
"""
|
20 |
+
Initialize the enhanced detector with a trained PyTorch model.
|
21 |
+
|
22 |
+
Args:
|
23 |
+
model_path: Path to the trained PyTorch model
|
24 |
+
"""
|
25 |
+
# Initialize the PyTorch model
|
26 |
+
self.model = PyTorchAIDetector(model_path)
|
27 |
+
|
28 |
+
def analyze_image(self, image_path):
|
29 |
+
"""
|
30 |
+
Analyze an image to detect if it's AI-generated using the PyTorch model.
|
31 |
+
|
32 |
+
Args:
|
33 |
+
image_path: Path to the image.
|
34 |
+
|
35 |
+
Returns:
|
36 |
+
Dictionary with analysis results.
|
37 |
+
"""
|
38 |
+
# Use the PyTorch model to analyze the image
|
39 |
+
try:
|
40 |
+
# Get the model prediction
|
41 |
+
result = self.model.analyze_image(image_path)
|
42 |
+
|
43 |
+
# Enhance the result with additional metadata
|
44 |
+
result.update({
|
45 |
+
"model_name": "Enhanced AI Image Detector",
|
46 |
+
"model_version": "1.0.0",
|
47 |
+
"confidence": float(result["overall_score"] if result["is_ai_generated"] else 1.0 - result["overall_score"])
|
48 |
+
})
|
49 |
+
|
50 |
+
return result
|
51 |
+
|
52 |
+
except Exception as e:
|
53 |
+
raise ValueError(f"Failed to analyze image: {str(e)}")
|
54 |
+
|
55 |
+
def _analyze_noise_patterns(self, cv_img):
|
56 |
+
"""
|
57 |
+
Analyze noise patterns in the image.
|
58 |
+
"""
|
59 |
+
# Convert to grayscale
|
60 |
+
gray = cv2.cvtColor(cv_img, cv2.COLOR_BGR2GRAY)
|
61 |
+
|
62 |
+
# Apply ELA (Error Level Analysis)
|
63 |
+
# Save image at 90% quality
|
64 |
+
temp_path = "temp_ela.jpg"
|
65 |
+
cv2.imwrite(temp_path, cv_img, [cv2.IMWRITE_JPEG_QUALITY, 90])
|
66 |
+
|
67 |
+
# Reload the image
|
68 |
+
compressed_img = cv2.imread(temp_path)
|
69 |
+
os.remove(temp_path)
|
70 |
+
|
71 |
+
# Calculate difference between original and compressed
|
72 |
+
if compressed_img is not None:
|
73 |
+
diff = cv2.absdiff(cv_img, compressed_img)
|
74 |
+
diff_gray = cv2.cvtColor(diff, cv2.COLOR_BGR2GRAY)
|
75 |
+
|
76 |
+
# Calculate difference statistics
|
77 |
+
mean_diff = np.mean(diff_gray)
|
78 |
+
std_diff = np.std(diff_gray)
|
79 |
+
|
80 |
+
# Normalize result to [0, 1] range
|
81 |
+
# Higher values indicate higher likelihood of AI-generated
|
82 |
+
ela_score = min(mean_diff / 8.0, 1.0)
|
83 |
+
else:
|
84 |
+
ela_score = 0.5 # Neutral value if ELA analysis fails
|
85 |
+
|
86 |
+
# Analyze noise using Laplacian filter
|
87 |
+
laplacian = cv2.Laplacian(gray, cv2.CV_64F)
|
88 |
+
laplacian_std = np.std(laplacian)
|
89 |
+
|
90 |
+
# Normalize result to [0, 1] range
|
91 |
+
# Lower values indicate higher likelihood of AI-generated
|
92 |
+
# (AI-generated images tend to be smoother)
|
93 |
+
laplacian_score = 1.0 - min(laplacian_std / 15.0, 1.0)
|
94 |
+
|
95 |
+
# Analyze noise in frequency domain
|
96 |
+
# Apply Fourier transform
|
97 |
+
f_transform = np.fft.fft2(gray)
|
98 |
+
f_shift = np.fft.fftshift(f_transform)
|
99 |
+
magnitude_spectrum = 20 * np.log(np.abs(f_shift) + 1)
|
100 |
+
|
101 |
+
# Calculate ratio of high to low frequencies
|
102 |
+
h, w = gray.shape
|
103 |
+
center_y, center_x = h // 2, w // 2
|
104 |
+
radius = min(center_y, center_x) // 4
|
105 |
+
|
106 |
+
# Create mask for low frequencies
|
107 |
+
y, x = np.ogrid[:h, :w]
|
108 |
+
low_freq_mask = ((y - center_y) ** 2 + (x - center_x) ** 2) <= radius ** 2
|
109 |
+
|
110 |
+
# Calculate average of low and high frequencies
|
111 |
+
low_freq_mean = np.mean(magnitude_spectrum[low_freq_mask])
|
112 |
+
high_freq_mean = np.mean(magnitude_spectrum[~low_freq_mask])
|
113 |
+
|
114 |
+
# Calculate ratio
|
115 |
+
freq_ratio = high_freq_mean / low_freq_mean if low_freq_mean > 0 else 0
|
116 |
+
|
117 |
+
# Normalize result to [0, 1] range
|
118 |
+
# Lower values indicate higher likelihood of AI-generated
|
119 |
+
freq_score = 1.0 - min(freq_ratio / 0.4, 1.0)
|
120 |
+
|
121 |
+
# Combine results with optimized weights
|
122 |
+
noise_score = 0.4 * ela_score + 0.35 * laplacian_score + 0.25 * freq_score
|
123 |
+
|
124 |
+
return noise_score
|
125 |
+
|
126 |
+
def _analyze_texture(self, cv_img):
|
127 |
+
"""
|
128 |
+
Analyze image texture.
|
129 |
+
"""
|
130 |
+
# Convert to grayscale
|
131 |
+
gray = cv2.cvtColor(cv_img, cv2.COLOR_BGR2GRAY)
|
132 |
+
|
133 |
+
# Calculate Gray Level Co-occurrence Matrix (GLCM)
|
134 |
+
distances = [1]
|
135 |
+
angles = [0, np.pi/4, np.pi/2, 3*np.pi/4]
|
136 |
+
|
137 |
+
# Use skimage to calculate GLCM
|
138 |
+
try:
|
139 |
+
glcm = feature.graycomatrix(gray, distances, angles, 256, symmetric=True, normed=True)
|
140 |
+
|
141 |
+
# Extract GLCM features
|
142 |
+
contrast = feature.graycoprops(glcm, 'contrast')[0, 0]
|
143 |
+
dissimilarity = feature.graycoprops(glcm, 'dissimilarity')[0, 0]
|
144 |
+
homogeneity = feature.graycoprops(glcm, 'homogeneity')[0, 0]
|
145 |
+
energy = feature.graycoprops(glcm, 'energy')[0, 0]
|
146 |
+
correlation = feature.graycoprops(glcm, 'correlation')[0, 0]
|
147 |
+
|
148 |
+
# Normalize values
|
149 |
+
contrast_norm = min(contrast / 80.0, 1.0)
|
150 |
+
dissimilarity_norm = min(dissimilarity / 8.0, 1.0)
|
151 |
+
homogeneity_norm = homogeneity
|
152 |
+
energy_norm = energy
|
153 |
+
correlation_norm = correlation
|
154 |
+
|
155 |
+
# AI-generated images tend to be more homogeneous and less contrasted
|
156 |
+
texture_score = (
|
157 |
+
0.15 * (1.0 - contrast_norm) +
|
158 |
+
0.15 * (1.0 - dissimilarity_norm) +
|
159 |
+
0.25 * homogeneity_norm +
|
160 |
+
0.25 * energy_norm +
|
161 |
+
0.20 * correlation_norm
|
162 |
+
)
|
163 |
+
except Exception:
|
164 |
+
# Use alternative analysis if GLCM fails
|
165 |
+
# Calculate image variance
|
166 |
+
variance = np.var(gray)
|
167 |
+
variance_norm = min(variance / 800.0, 1.0)
|
168 |
+
|
169 |
+
# Calculate entropy
|
170 |
+
hist = cv2.calcHist([gray], [0], None, [256], [0, 256])
|
171 |
+
hist = hist / hist.sum()
|
172 |
+
entropy = -np.sum(hist * np.log2(hist + 1e-10))
|
173 |
+
entropy_norm = min(entropy / 7.5, 1.0)
|
174 |
+
|
175 |
+
# AI-generated images tend to have less variance and entropy
|
176 |
+
texture_score = 0.6 * (1.0 - variance_norm) + 0.4 * (1.0 - entropy_norm)
|
177 |
+
|
178 |
+
return texture_score
|
179 |
+
|
180 |
+
def _analyze_color_coherence(self, cv_img):
|
181 |
+
"""
|
182 |
+
Analyze color coherence in the image.
|
183 |
+
"""
|
184 |
+
# Split image into BGR channels
|
185 |
+
b, g, r = cv2.split(cv_img)
|
186 |
+
|
187 |
+
# Calculate standard deviation for each channel
|
188 |
+
std_b = np.std(b)
|
189 |
+
std_g = np.std(g)
|
190 |
+
std_r = np.std(r)
|
191 |
+
|
192 |
+
# Calculate average standard deviation
|
193 |
+
avg_std = (std_b + std_g + std_r) / 3.0
|
194 |
+
|
195 |
+
# Normalize result to [0, 1] range
|
196 |
+
# Lower values indicate higher likelihood of AI-generated
|
197 |
+
std_score = 1.0 - min(avg_std / 45.0, 1.0)
|
198 |
+
|
199 |
+
# Analyze color distribution
|
200 |
+
hist_b = cv2.calcHist([b], [0], None, [64], [0, 256])
|
201 |
+
hist_g = cv2.calcHist([g], [0], None, [64], [0, 256])
|
202 |
+
hist_r = cv2.calcHist([r], [0], None, [64], [0, 256])
|
203 |
+
|
204 |
+
# Normalize histograms
|
205 |
+
hist_b = hist_b / hist_b.sum()
|
206 |
+
hist_g = hist_g / hist_g.sum()
|
207 |
+
hist_r = hist_r / hist_r.sum()
|
208 |
+
|
209 |
+
# Calculate histogram entropy
|
210 |
+
entropy_b = -np.sum(hist_b * np.log2(hist_b + 1e-10))
|
211 |
+
entropy_g = -np.sum(hist_g * np.log2(hist_g + 1e-10))
|
212 |
+
entropy_r = -np.sum(hist_r * np.log2(hist_r + 1e-10))
|
213 |
+
|
214 |
+
# Calculate average entropy
|
215 |
+
avg_entropy = (entropy_b + entropy_g + entropy_r) / 3.0
|
216 |
+
|
217 |
+
# Normalize result to [0, 1] range
|
218 |
+
# Lower values indicate higher likelihood of AI-generated
|
219 |
+
entropy_score = 1.0 - min(avg_entropy / 5.8, 1.0)
|
220 |
+
|
221 |
+
# Analyze channel relationships
|
222 |
+
correlation_bg = np.corrcoef(b.flatten(), g.flatten())[0, 1]
|
223 |
+
correlation_br = np.corrcoef(b.flatten(), r.flatten())[0, 1]
|
224 |
+
correlation_gr = np.corrcoef(g.flatten(), r.flatten())[0, 1]
|
225 |
+
|
226 |
+
# Calculate average correlation
|
227 |
+
avg_correlation = (abs(correlation_bg) + abs(correlation_br) + abs(correlation_gr)) / 3.0
|
228 |
+
|
229 |
+
# Normalize result to [0, 1] range
|
230 |
+
# Higher values indicate higher likelihood of AI-generated
|
231 |
+
correlation_score = min(avg_correlation, 1.0)
|
232 |
+
|
233 |
+
# Combine results with optimized weights
|
234 |
+
color_score = 0.25 * std_score + 0.25 * entropy_score + 0.5 * correlation_score
|
235 |
+
|
236 |
+
return color_score
|
237 |
+
|
238 |
+
def _analyze_edges(self, cv_img):
|
239 |
+
"""
|
240 |
+
Analyze edges in the image.
|
241 |
+
"""
|
242 |
+
# Convert to grayscale
|
243 |
+
gray = cv2.cvtColor(cv_img, cv2.COLOR_BGR2GRAY)
|
244 |
+
|
245 |
+
# Detect edges using Canny
|
246 |
+
edges = cv2.Canny(gray, 100, 200)
|
247 |
+
|
248 |
+
# Calculate ratio of edge pixels
|
249 |
+
edge_ratio = np.count_nonzero(edges) / edges.size
|
250 |
+
|
251 |
+
# Normalize result to [0, 1] range
|
252 |
+
# Lower values indicate higher likelihood of AI-generated
|
253 |
+
edge_ratio_score = 1.0 - min(edge_ratio / 0.08, 1.0)
|
254 |
+
|
255 |
+
# Analyze edge consistency
|
256 |
+
# Apply Sobel filter
|
257 |
+
sobelx = cv2.Sobel(gray, cv2.CV_64F, 1, 0, ksize=3)
|
258 |
+
sobely = cv2.Sobel(gray, cv2.CV_64F, 0, 1, ksize=3)
|
259 |
+
|
260 |
+
# Calculate gradient direction
|
261 |
+
gradient_direction = np.arctan2(sobely, sobelx)
|
262 |
+
|
263 |
+
# Calculate histogram of gradient directions
|
264 |
+
hist, _ = np.histogram(gradient_direction, bins=36, range=(-np.pi, np.pi))
|
265 |
+
hist = hist / hist.sum()
|
266 |
+
|
267 |
+
# Calculate histogram entropy
|
268 |
+
entropy = -np.sum(hist * np.log2(hist + 1e-10))
|
269 |
+
|
270 |
+
# Normalize result to [0, 1] range
|
271 |
+
# Lower values indicate higher likelihood of AI-generated
|
272 |
+
entropy_score = 1.0 - min(entropy / 5.0, 1.0)
|
273 |
+
|
274 |
+
# Additional analysis: edge consistency
|
275 |
+
# Calculate gradient magnitude
|
276 |
+
gradient_magnitude = np.sqrt(sobelx**2 + sobely**2)
|
277 |
+
|
278 |
+
# Calculate standard deviation of gradient magnitude
|
279 |
+
gradient_std = np.std(gradient_magnitude)
|
280 |
+
|
281 |
+
# Normalize result to [0, 1] range
|
282 |
+
# Lower values indicate higher likelihood of AI-generated
|
283 |
+
gradient_score = 1.0 - min(gradient_std / 30.0, 1.0)
|
284 |
+
|
285 |
+
# Combine results with optimized weights
|
286 |
+
edge_score = 0.4 * edge_ratio_score + 0.3 * entropy_score + 0.3 * gradient_score
|
287 |
+
|
288 |
+
return edge_score
|
289 |
+
|
290 |
+
def _analyze_faces(self, cv_img):
|
291 |
+
"""
|
292 |
+
Analyze faces in the image.
|
293 |
+
"""
|
294 |
+
# Convert to grayscale
|
295 |
+
gray = cv2.cvtColor(cv_img, cv2.COLOR_BGR2GRAY)
|
296 |
+
|
297 |
+
# Detect faces
|
298 |
+
faces = self.face_cascade.detectMultiScale(gray, 1.3, 5)
|
299 |
+
|
300 |
+
if len(faces) == 0:
|
301 |
+
return None
|
302 |
+
|
303 |
+
face_scores = []
|
304 |
+
|
305 |
+
for (x, y, w, h) in faces:
|
306 |
+
# Extract face region
|
307 |
+
face_roi = gray[y:y+h, x:x+w]
|
308 |
+
|
309 |
+
# Detect eyes
|
310 |
+
eyes = self.eye_cascade.detectMultiScale(face_roi)
|
311 |
+
|
312 |
+
# Analyze face symmetry
|
313 |
+
symmetry_score = self._analyze_face_symmetry(face_roi)
|
314 |
+
|
315 |
+
# Analyze face texture
|
316 |
+
texture_score = self._analyze_face_texture(face_roi)
|
317 |
+
|
318 |
+
# Analyze face proportions
|
319 |
+
proportion_score = self._analyze_face_proportions(face_roi, eyes)
|
320 |
+
|
321 |
+
# Additional analysis: face details
|
322 |
+
detail_score = self._analyze_face_details(face_roi)
|
323 |
+
|
324 |
+
# Combine results with optimized weights
|
325 |
+
face_score = 0.3 * symmetry_score + 0.25 * texture_score + 0.25 * proportion_score + 0.2 * detail_score
|
326 |
+
face_scores.append(face_score)
|
327 |
+
|
328 |
+
# Calculate average face score
|
329 |
+
avg_face_score = np.mean(face_scores) if face_scores else 0.5
|
330 |
+
|
331 |
+
return avg_face_score
|
332 |
+
|
333 |
+
def _analyze_face_symmetry(self, face_roi):
|
334 |
+
"""
|
335 |
+
Analyze face symmetry.
|
336 |
+
"""
|
337 |
+
# Split face into halves
|
338 |
+
h, w = face_roi.shape
|
339 |
+
midpoint = w // 2
|
340 |
+
|
341 |
+
left_half = face_roi[:, :midpoint]
|
342 |
+
right_half = face_roi[:, midpoint:]
|
343 |
+
|
344 |
+
# Flip right half
|
345 |
+
right_half_flipped = cv2.flip(right_half, 1)
|
346 |
+
|
347 |
+
# Resize flipped right half to match left half
|
348 |
+
if left_half.shape[1] != right_half_flipped.shape[1]:
|
349 |
+
right_half_flipped = cv2.resize(right_half_flipped, (left_half.shape[1], left_half.shape[0]))
|
350 |
+
|
351 |
+
# Calculate difference between halves
|
352 |
+
diff = cv2.absdiff(left_half, right_half_flipped)
|
353 |
+
|
354 |
+
# Calculate average difference
|
355 |
+
mean_diff = np.mean(diff)
|
356 |
+
|
357 |
+
# Normalize result to [0, 1] range
|
358 |
+
# Lower values indicate more symmetry
|
359 |
+
# AI-generated faces tend to be more symmetrical
|
360 |
+
symmetry_score = 1.0 - min(mean_diff / 25.0, 1.0)
|
361 |
+
|
362 |
+
return symmetry_score
|
363 |
+
|
364 |
+
def _analyze_face_texture(self, face_roi):
|
365 |
+
"""
|
366 |
+
Analyze face texture.
|
367 |
+
"""
|
368 |
+
# Apply Laplacian filter to detect details
|
369 |
+
laplacian = cv2.Laplacian(face_roi, cv2.CV_64F)
|
370 |
+
laplacian_std = np.std(laplacian)
|
371 |
+
|
372 |
+
# Normalize result to [0, 1] range
|
373 |
+
# Lower values indicate fewer details
|
374 |
+
# AI-generated faces tend to have fewer details
|
375 |
+
texture_score = 1.0 - min(laplacian_std / 15.0, 1.0)
|
376 |
+
|
377 |
+
return texture_score
|
378 |
+
|
379 |
+
def _analyze_face_proportions(self, face_roi, eyes):
|
380 |
+
"""
|
381 |
+
Analyze face proportions.
|
382 |
+
"""
|
383 |
+
h, w = face_roi.shape
|
384 |
+
|
385 |
+
if len(eyes) >= 2:
|
386 |
+
# Sort eyes from left to right
|
387 |
+
eyes = sorted(eyes, key=lambda e: e[0])
|
388 |
+
|
389 |
+
# Calculate distance between eyes
|
390 |
+
eye1_center = (eyes[0][0] + eyes[0][2] // 2, eyes[0][1] + eyes[0][3] // 2)
|
391 |
+
eye2_center = (eyes[1][0] + eyes[1][2] // 2, eyes[1][1] + eyes[1][3] // 2)
|
392 |
+
|
393 |
+
eye_distance = np.sqrt((eye2_center[0] - eye1_center[0]) ** 2 + (eye2_center[1] - eye1_center[1]) ** 2)
|
394 |
+
|
395 |
+
# Calculate ratio of eye distance to face width
|
396 |
+
eye_ratio = eye_distance / w
|
397 |
+
|
398 |
+
# Analyze ratio
|
399 |
+
# In natural faces, eye distance is about 0.3 of face width
|
400 |
+
proportion_score = 1.0 - min(abs(eye_ratio - 0.3) / 0.2, 1.0)
|
401 |
+
else:
|
402 |
+
# If two eyes are not found, use neutral value
|
403 |
+
proportion_score = 0.5
|
404 |
+
|
405 |
+
return proportion_score
|
406 |
+
|
407 |
+
def _analyze_face_details(self, face_roi):
|
408 |
+
"""
|
409 |
+
Analyze face details.
|
410 |
+
"""
|
411 |
+
# Apply Gabor filter to detect fine details
|
412 |
+
# Create Gabor filter
|
413 |
+
ksize = 31
|
414 |
+
sigma = 4.0
|
415 |
+
theta = 0
|
416 |
+
lambd = 10.0
|
417 |
+
gamma = 0.5
|
418 |
+
|
419 |
+
# Apply Gabor filter at different angles
|
420 |
+
angles = [0, np.pi/4, np.pi/2, 3*np.pi/4]
|
421 |
+
responses = []
|
422 |
+
|
423 |
+
for theta in angles:
|
424 |
+
kernel = cv2.getGaborKernel((ksize, ksize), sigma, theta, lambd, gamma, 0, ktype=cv2.CV_32F)
|
425 |
+
filtered = cv2.filter2D(face_roi, cv2.CV_8UC3, kernel)
|
426 |
+
responses.append(filtered)
|
427 |
+
|
428 |
+
# Combine responses
|
429 |
+
combined_response = np.zeros_like(face_roi)
|
430 |
+
for response in responses:
|
431 |
+
combined_response = cv2.add(combined_response, response)
|
432 |
+
|
433 |
+
# Calculate standard deviation of combined response
|
434 |
+
response_std = np.std(combined_response)
|
435 |
+
|
436 |
+
# Normalize result to [0, 1] range
|
437 |
+
# Lower values indicate fewer details
|
438 |
+
# AI-generated faces tend to have fewer details
|
439 |
+
detail_score = 1.0 - min(response_std / 25.0, 1.0)
|
440 |
+
|
441 |
+
return detail_score
|
442 |
+
|
443 |
+
def _analyze_additional_features(self, cv_img):
|
444 |
+
"""
|
445 |
+
Analyze additional features for borderline images.
|
446 |
+
"""
|
447 |
+
# Convert to grayscale
|
448 |
+
gray = cv2.cvtColor(cv_img, cv2.COLOR_BGR2GRAY)
|
449 |
+
|
450 |
+
# Analyze repetition in the image
|
451 |
+
# Apply Fourier transform
|
452 |
+
f_transform = np.fft.fft2(gray)
|
453 |
+
f_shift = np.fft.fftshift(f_transform)
|
454 |
+
magnitude_spectrum = np.log(np.abs(f_shift) + 1)
|
455 |
+
|
456 |
+
# Calculate standard deviation of spectrum
|
457 |
+
spectrum_std = np.std(magnitude_spectrum)
|
458 |
+
|
459 |
+
# Normalize result to [0, 1] range
|
460 |
+
# Lower values indicate higher likelihood of AI-generated
|
461 |
+
spectrum_score = 1.0 - min(spectrum_std / 2.0, 1.0)
|
462 |
+
|
463 |
+
# Analyze color gradients
|
464 |
+
# Convert to HSV space
|
465 |
+
hsv = cv2.cvtColor(cv_img, cv2.COLOR_BGR2HSV)
|
466 |
+
h, s, v = cv2.split(hsv)
|
467 |
+
|
468 |
+
# Calculate histogram of saturation channel
|
469 |
+
hist_s = cv2.calcHist([s], [0], None, [64], [0, 256])
|
470 |
+
hist_s = hist_s / hist_s.sum()
|
471 |
+
|
472 |
+
# Calculate histogram entropy
|
473 |
+
entropy_s = -np.sum(hist_s * np.log2(hist_s + 1e-10))
|
474 |
+
|
475 |
+
# Normalize result to [0, 1] range
|
476 |
+
# Lower values indicate higher likelihood of AI-generated
|
477 |
+
entropy_score = 1.0 - min(entropy_s / 5.0, 1.0)
|
478 |
+
|
479 |
+
# Combine results
|
480 |
+
additional_score = 0.6 * spectrum_score + 0.4 * entropy_score
|
481 |
+
|
482 |
+
return additional_score
|
app.py
ADDED
@@ -0,0 +1,119 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""
|
2 |
+
Hugging Face Spaces inference API for Enhanced AI Image Detector
|
3 |
+
"""
|
4 |
+
|
5 |
+
import os
|
6 |
+
import sys
|
7 |
+
import gradio as gr
|
8 |
+
import numpy as np
|
9 |
+
from PIL import Image
|
10 |
+
import cv2
|
11 |
+
import tempfile
|
12 |
+
from ai_detector import EnhancedAIDetector
|
13 |
+
|
14 |
+
# Initialize the detector
|
15 |
+
detector = EnhancedAIDetector()
|
16 |
+
|
17 |
+
def analyze_image(image):
|
18 |
+
"""
|
19 |
+
Analyze an image using the Enhanced AI Image Detector with PyTorch model
|
20 |
+
|
21 |
+
Args:
|
22 |
+
image: Image uploaded through Gradio interface
|
23 |
+
|
24 |
+
Returns:
|
25 |
+
Analysis results and visualization
|
26 |
+
"""
|
27 |
+
# Save the image to a temporary file
|
28 |
+
with tempfile.NamedTemporaryFile(suffix='.jpg', delete=False) as temp_file:
|
29 |
+
temp_path = temp_file.name
|
30 |
+
|
31 |
+
# Save the PIL image to the temporary file
|
32 |
+
if isinstance(image, np.ndarray):
|
33 |
+
cv2.imwrite(temp_path, cv2.cvtColor(image, cv2.COLOR_RGB2BGR))
|
34 |
+
else:
|
35 |
+
image.save(temp_path)
|
36 |
+
|
37 |
+
try:
|
38 |
+
# Analyze the image
|
39 |
+
result = detector.analyze_image(temp_path)
|
40 |
+
|
41 |
+
# Clean up the temporary file
|
42 |
+
os.unlink(temp_path)
|
43 |
+
|
44 |
+
# Prepare the results
|
45 |
+
is_ai = result["is_ai_generated"]
|
46 |
+
score = result["overall_score"]
|
47 |
+
confidence = result.get("confidence", score if is_ai else 1.0 - score)
|
48 |
+
|
49 |
+
# Create result message
|
50 |
+
if is_ai:
|
51 |
+
message = f"🤖 This image is likely AI-generated (Confidence: {confidence:.2f})"
|
52 |
+
else:
|
53 |
+
message = f"📷 This image is likely authentic (Confidence: {confidence:.2f})"
|
54 |
+
|
55 |
+
# Create detailed analysis
|
56 |
+
detailed_analysis = f"""
|
57 |
+
### Detailed Analysis:
|
58 |
+
|
59 |
+
| Property | Value |
|
60 |
+
|----------|-------|
|
61 |
+
| Overall Score | {result['overall_score']:.2f} |
|
62 |
+
| Confidence | {confidence:.2f} |
|
63 |
+
| Model | {result.get('model_name', 'Enhanced AI Image Detector')} |
|
64 |
+
| Version | {result.get('model_version', '1.0.0')} |
|
65 |
+
"""
|
66 |
+
|
67 |
+
return message, detailed_analysis
|
68 |
+
|
69 |
+
except Exception as e:
|
70 |
+
# Clean up the temporary file if it exists
|
71 |
+
if os.path.exists(temp_path):
|
72 |
+
os.unlink(temp_path)
|
73 |
+
|
74 |
+
error_message = f"Error analyzing image: {str(e)}"
|
75 |
+
return error_message, ""
|
76 |
+
|
77 |
+
# Create the Gradio interface
|
78 |
+
with gr.Blocks(title="Enhanced AI Image Detector") as demo:
|
79 |
+
gr.Markdown("# Enhanced AI Image Detector")
|
80 |
+
gr.Markdown("Upload an image to determine if it's real or AI-generated.")
|
81 |
+
|
82 |
+
with gr.Row():
|
83 |
+
with gr.Column():
|
84 |
+
input_image = gr.Image(type="pil", label="Upload Image")
|
85 |
+
analyze_button = gr.Button("Analyze Image")
|
86 |
+
|
87 |
+
with gr.Column():
|
88 |
+
result_text = gr.Textbox(label="Result")
|
89 |
+
detailed_output = gr.Markdown(label="Detailed Analysis")
|
90 |
+
|
91 |
+
analyze_button.click(
|
92 |
+
fn=analyze_image,
|
93 |
+
inputs=[input_image],
|
94 |
+
outputs=[result_text, detailed_output]
|
95 |
+
)
|
96 |
+
|
97 |
+
gr.Markdown("""
|
98 |
+
## How it works
|
99 |
+
|
100 |
+
This model uses a trained PyTorch neural network to detect AI-generated images. The model has been trained on a large dataset of real and AI-generated images to learn the subtle differences between them.
|
101 |
+
|
102 |
+
The model was trained using a deep convolutional neural network architecture that can detect patterns that are often invisible to the human eye. These patterns include:
|
103 |
+
|
104 |
+
1. **Noise and artifact patterns** specific to AI generation methods
|
105 |
+
2. **Texture inconsistencies** that appear in AI-generated content
|
106 |
+
3. **Color and lighting anomalies** common in synthetic images
|
107 |
+
4. **Structural patterns** that differ from natural photographs
|
108 |
+
|
109 |
+
## Limitations
|
110 |
+
|
111 |
+
- The model may struggle with highly realistic AI-generated images from newer generation models
|
112 |
+
- Some real images with unusual characteristics may be misclassified
|
113 |
+
- Performance depends on image quality and resolution
|
114 |
+
- The model works best with images similar to those in its training dataset
|
115 |
+
""")
|
116 |
+
|
117 |
+
# Launch the app
|
118 |
+
if __name__ == "__main__":
|
119 |
+
demo.launch()
|
example.py
ADDED
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""
|
2 |
+
Example usage of the Enhanced AI Image Detector
|
3 |
+
"""
|
4 |
+
|
5 |
+
import os
|
6 |
+
import sys
|
7 |
+
import argparse
|
8 |
+
from ai_detector import EnhancedAIDetector
|
9 |
+
import json
|
10 |
+
|
11 |
+
def main():
|
12 |
+
# Parse command line arguments
|
13 |
+
parser = argparse.ArgumentParser(description='Detect if an image is AI-generated')
|
14 |
+
parser.add_argument('image_path', type=str, help='Path to the image to analyze')
|
15 |
+
parser.add_argument('--output', type=str, help='Path to save the results as JSON', default=None)
|
16 |
+
parser.add_argument('--detailed', action='store_true', help='Show detailed analysis results')
|
17 |
+
args = parser.parse_args()
|
18 |
+
|
19 |
+
# Check if image exists
|
20 |
+
if not os.path.exists(args.image_path):
|
21 |
+
print(f"Error: Image not found at {args.image_path}")
|
22 |
+
sys.exit(1)
|
23 |
+
|
24 |
+
# Initialize the detector
|
25 |
+
detector = EnhancedAIDetector()
|
26 |
+
|
27 |
+
try:
|
28 |
+
# Analyze the image
|
29 |
+
print(f"Analyzing image: {args.image_path}")
|
30 |
+
result = detector.analyze_image(args.image_path)
|
31 |
+
|
32 |
+
# Display the result
|
33 |
+
if result["is_ai_generated"]:
|
34 |
+
print("\n🤖 This image is likely AI-generated")
|
35 |
+
print(f"Confidence score: {result['overall_score']:.2f}")
|
36 |
+
else:
|
37 |
+
print("\n📷 This image is likely authentic")
|
38 |
+
print(f"Confidence score: {1 - result['overall_score']:.2f}")
|
39 |
+
|
40 |
+
# Display detailed analysis if requested
|
41 |
+
if args.detailed:
|
42 |
+
print("\nDetailed Analysis:")
|
43 |
+
print(f"Noise analysis score: {result['noise_analysis']['score']:.2f} " +
|
44 |
+
("(suspicious)" if result['noise_analysis']['is_suspicious'] else "(normal)"))
|
45 |
+
print(f"Texture analysis score: {result['texture_analysis']['score']:.2f} " +
|
46 |
+
("(suspicious)" if result['texture_analysis']['is_suspicious'] else "(normal)"))
|
47 |
+
print(f"Color analysis score: {result['color_analysis']['score']:.2f} " +
|
48 |
+
("(suspicious)" if result['color_analysis']['is_suspicious'] else "(normal)"))
|
49 |
+
print(f"Edge analysis score: {result['edge_analysis']['score']:.2f} " +
|
50 |
+
("(suspicious)" if result['edge_analysis']['is_suspicious'] else "(normal)"))
|
51 |
+
|
52 |
+
if "face_analysis" in result:
|
53 |
+
print(f"Face analysis score: {result['face_analysis']['score']:.2f} " +
|
54 |
+
("(suspicious)" if result['face_analysis']['is_suspicious'] else "(normal)"))
|
55 |
+
|
56 |
+
# Save results to JSON if requested
|
57 |
+
if args.output:
|
58 |
+
with open(args.output, 'w', encoding='utf-8') as f:
|
59 |
+
json.dump(result, f, ensure_ascii=False, indent=2)
|
60 |
+
print(f"\nResults saved to {args.output}")
|
61 |
+
|
62 |
+
except Exception as e:
|
63 |
+
print(f"Error analyzing image: {str(e)}")
|
64 |
+
sys.exit(1)
|
65 |
+
|
66 |
+
if __name__ == "__main__":
|
67 |
+
main()
|
model-card.md
ADDED
@@ -0,0 +1,113 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
---
|
2 |
+
language:
|
3 |
+
- ar
|
4 |
+
- en
|
5 |
+
library_name: custom
|
6 |
+
tags:
|
7 |
+
- computer-vision
|
8 |
+
- image-classification
|
9 |
+
- ai-detection
|
10 |
+
- ai-generated-content
|
11 |
+
- deepfake-detection
|
12 |
+
datasets:
|
13 |
+
- custom
|
14 |
+
license: mit
|
15 |
+
---
|
16 |
+
|
17 |
+
# Enhanced AI Image Detector (كاشف الصور المنشأة بالذكاء الاصطناعي المحسن)
|
18 |
+
|
19 |
+
هذا النموذج يكتشف ما إذا كانت الصورة حقيقية أم منشأة بواسطة الذكاء الاصطناعي باستخدام تقنيات متعددة لرؤية الحاسوب.
|
20 |
+
|
21 |
+
This model detects whether an image is real or AI-generated using multiple computer vision techniques.
|
22 |
+
|
23 |
+
## Model Description (وصف النموذج)
|
24 |
+
|
25 |
+
يستخدم كاشف الصور المنشأة بالذكاء الاصطناعي المحسن مجموعة من تقنيات رؤية الكمبيوتر لتحليل جوانب مختلفة من الصور وتحديد ما إذا كانت صورًا فوتوغرافية أصلية أو تم إنشاؤها بواسطة أدوات الذكاء الاصطناعي مثل DALL-E أو Midjourney أو Stable Diffusion.
|
26 |
+
|
27 |
+
The Enhanced AI Image Detector uses a combination of computer vision techniques to analyze various aspects of images and determine whether they are authentic photographs or generated by AI tools like DALL-E, Midjourney, or Stable Diffusion.
|
28 |
+
|
29 |
+
### Key Features (الميزات الرئيسية)
|
30 |
+
|
31 |
+
- **تحليل متعدد الجوانب**: يفحص أنماط الضوضاء والملمس واتساق الألوان والحواف وميزات الوجه
|
32 |
+
- **لا يتطلب التعلم العميق**: يستخدم تقنيات رؤية الكمبيوتر التقليدية دون الحاجة إلى موارد وحدة معالجة الرسومات
|
33 |
+
- **نتائج مفصلة**: يوفر تحليلًا شاملاً مع درجات لكل طريقة كشف
|
34 |
+
- **كشف يدرك الوجوه**: تحليل خاص للصور التي تحتوي على وجوه، والتي غالبًا ما تكون حالات صعبة
|
35 |
+
|
36 |
+
- **Multi-faceted Analysis**: Examines noise patterns, texture, color coherence, edges, and facial features
|
37 |
+
- **No Deep Learning Required**: Uses traditional computer vision techniques without requiring GPU resources
|
38 |
+
- **Detailed Results**: Provides comprehensive analysis with scores for each detection method
|
39 |
+
- **Face-aware Detection**: Special analysis for images containing faces, which are often challenging cases
|
40 |
+
|
41 |
+
## How It Works (كيف يعمل)
|
42 |
+
|
43 |
+
يحلل النموذج العديد من خصائص الصورة التي تختلف عادة بين الصور الحقيقية والصور المنشأة بالذكاء الاصطناعي:
|
44 |
+
|
45 |
+
The model analyzes several image characteristics that typically differ between real and AI-generated images:
|
46 |
+
|
47 |
+
1. **تحليل أنماط الضوضاء**:
|
48 |
+
- تحليل مستوى الخطأ (ELA)
|
49 |
+
- كشف الضوضاء باستخدام مرشح لابلاس
|
50 |
+
- تحليل المجال الترددي
|
51 |
+
|
52 |
+
2. **تحليل الملمس**:
|
53 |
+
- ميزات مصفوفة التواجد المشترك للمستويات الرمادية (GLCM)
|
54 |
+
- تجانس الملمس والتباين
|
55 |
+
|
56 |
+
3. **تحليل اتساق الألوان**:
|
57 |
+
- توزيع الألوان والانتروبيا
|
58 |
+
- الارتباطات بين القنوات
|
59 |
+
|
60 |
+
4. **تحليل الحواف**:
|
61 |
+
- اتساق الحواف وأنماطها
|
62 |
+
- توزيع اتجاه التدرج
|
63 |
+
|
64 |
+
5. **تحليل الوجوه** (عند وجود وجوه):
|
65 |
+
- تناظر الوجه
|
66 |
+
- اتساق الملمس والتفاصيل
|
67 |
+
- نسب الوجه
|
68 |
+
|
69 |
+
## Usage (الاستخدام)
|
70 |
+
|
71 |
+
```python
|
72 |
+
from ai_detector import EnhancedAIDetector
|
73 |
+
|
74 |
+
# تهيئة الكاشف
|
75 |
+
detector = EnhancedAIDetector()
|
76 |
+
|
77 |
+
# تحليل صورة
|
78 |
+
result = detector.analyze_image("path/to/image.jpg")
|
79 |
+
|
80 |
+
# التحقق من النتيجة
|
81 |
+
if result["is_ai_generated"]:
|
82 |
+
print("هذه الصورة على الأرجح منشأة بالذكاء الاصطناعي")
|
83 |
+
print(f"درجة الثقة: {result['overall_score']:.2f}")
|
84 |
+
else:
|
85 |
+
print("هذه الصورة على الأرجح حقيقية")
|
86 |
+
print(f"درجة الثقة: {1 - result['overall_score']:.2f}")
|
87 |
+
|
88 |
+
# عرض التحليل المفصل
|
89 |
+
print(f"نتيجة تحليل الضوضاء: {result['noise_analysis']['score']:.2f}")
|
90 |
+
print(f"نتيجة تحليل الملمس: {result['texture_analysis']['score']:.2f}")
|
91 |
+
print(f"نتيجة تحليل الألوان: {result['color_analysis']['score']:.2f}")
|
92 |
+
print(f"نتيجة تحليل الحواف: {result['edge_analysis']['score']:.2f}")
|
93 |
+
if "face_analysis" in result:
|
94 |
+
print(f"نتيجة تحليل الوجه: {result['face_analysis']['score']:.2f}")
|
95 |
+
```
|
96 |
+
|
97 |
+
## Limitations (القيود)
|
98 |
+
|
99 |
+
- قد يواجه النموذج صعوبة مع الصور المنشأة بالذكاء الاصطناعي عالية الواقعية
|
100 |
+
- قد يتم تصنيف بعض الصور الحقيقية ذات الخصائص غير العادية بشكل خاطئ
|
101 |
+
- يعتمد الأداء على جودة الصورة ودقتها
|
102 |
+
- يتطلب تحليل الوجه وجود وجوه مرئية بتفاصيل كافية
|
103 |
+
|
104 |
+
- The model may struggle with highly realistic AI-generated images
|
105 |
+
- Some real images with unusual characteristics may be misclassified
|
106 |
+
- Performance depends on image quality and resolution
|
107 |
+
- Face analysis requires visible faces with sufficient detail
|
108 |
+
|
109 |
+
## Performance (الأداء)
|
110 |
+
|
111 |
+
تم اختبار النموذج على مجموعة متنوعة من الصور الحقيقية والمنشأة بالذكاء الاصطناعي، وحقق دقة إجمالية تزيد عن 85٪. يعمل النموذج بشكل أفضل مع الصور عالية الجودة وقد يواجه صعوبة مع الصور ذات الجودة المنخفضة أو المضغوطة بشدة.
|
112 |
+
|
113 |
+
The model has been tested on a diverse set of real and AI-generated images, achieving an overall accuracy of over 85%. The model performs best with high-quality images and may struggle with low-quality or heavily compressed images.
|
pytorch_model.py
ADDED
@@ -0,0 +1,101 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""
|
2 |
+
PyTorch model implementation for AI image detection
|
3 |
+
"""
|
4 |
+
|
5 |
+
import torch
|
6 |
+
import torch.nn as nn
|
7 |
+
import torch.nn.functional as F
|
8 |
+
import torchvision.transforms as transforms
|
9 |
+
from PIL import Image
|
10 |
+
import numpy as np
|
11 |
+
import os
|
12 |
+
|
13 |
+
# Define the model architecture
|
14 |
+
class AIDetectorModel(nn.Module):
|
15 |
+
def __init__(self):
|
16 |
+
super(AIDetectorModel, self).__init__()
|
17 |
+
# Define a typical CNN architecture
|
18 |
+
# Note: This is a placeholder architecture and should match your actual model architecture
|
19 |
+
self.conv1 = nn.Conv2d(3, 64, kernel_size=3, padding=1)
|
20 |
+
self.conv2 = nn.Conv2d(64, 128, kernel_size=3, padding=1)
|
21 |
+
self.conv3 = nn.Conv2d(128, 256, kernel_size=3, padding=1)
|
22 |
+
self.pool = nn.MaxPool2d(2, 2)
|
23 |
+
self.fc1 = nn.Linear(256 * 28 * 28, 512)
|
24 |
+
self.fc2 = nn.Linear(512, 2) # 2 classes: real or AI-generated
|
25 |
+
self.dropout = nn.Dropout(0.5)
|
26 |
+
|
27 |
+
def forward(self, x):
|
28 |
+
x = self.pool(F.relu(self.conv1(x)))
|
29 |
+
x = self.pool(F.relu(self.conv2(x)))
|
30 |
+
x = self.pool(F.relu(self.conv3(x)))
|
31 |
+
x = x.view(-1, 256 * 28 * 28) # Flatten
|
32 |
+
x = F.relu(self.fc1(x))
|
33 |
+
x = self.dropout(x)
|
34 |
+
x = self.fc2(x)
|
35 |
+
return x
|
36 |
+
|
37 |
+
class PyTorchAIDetector:
|
38 |
+
def __init__(self, model_path='best_model_improved.pth'):
|
39 |
+
"""
|
40 |
+
Initialize the PyTorch-based AI image detector
|
41 |
+
|
42 |
+
Args:
|
43 |
+
model_path: Path to the trained model file
|
44 |
+
"""
|
45 |
+
# Check if CUDA is available
|
46 |
+
self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
47 |
+
|
48 |
+
# Initialize the model
|
49 |
+
self.model = AIDetectorModel()
|
50 |
+
|
51 |
+
# Load the trained weights
|
52 |
+
model_path = os.path.join(os.path.dirname(__file__), model_path)
|
53 |
+
self.model.load_state_dict(torch.load(model_path, map_location=self.device))
|
54 |
+
self.model.to(self.device)
|
55 |
+
self.model.eval() # Set to evaluation mode
|
56 |
+
|
57 |
+
# Define image transformations
|
58 |
+
self.transform = transforms.Compose([
|
59 |
+
transforms.Resize((224, 224)),
|
60 |
+
transforms.ToTensor(),
|
61 |
+
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
|
62 |
+
])
|
63 |
+
|
64 |
+
def analyze_image(self, image_path):
|
65 |
+
"""
|
66 |
+
Analyze an image to detect if it's AI-generated
|
67 |
+
|
68 |
+
Args:
|
69 |
+
image_path: Path to the image
|
70 |
+
|
71 |
+
Returns:
|
72 |
+
Dictionary with analysis results
|
73 |
+
"""
|
74 |
+
try:
|
75 |
+
# Load and preprocess the image
|
76 |
+
image = Image.open(image_path).convert('RGB')
|
77 |
+
image_tensor = self.transform(image).unsqueeze(0).to(self.device)
|
78 |
+
|
79 |
+
# Make prediction
|
80 |
+
with torch.no_grad():
|
81 |
+
outputs = self.model(image_tensor)
|
82 |
+
probabilities = F.softmax(outputs, dim=1)
|
83 |
+
|
84 |
+
# Get the probability of being AI-generated (assuming class 1 is AI-generated)
|
85 |
+
ai_score = probabilities[0, 1].item()
|
86 |
+
|
87 |
+
# Determine if the image is AI-generated
|
88 |
+
is_ai_generated = ai_score > 0.5
|
89 |
+
|
90 |
+
# Prepare results
|
91 |
+
results = {
|
92 |
+
"image_path": image_path,
|
93 |
+
"overall_score": float(ai_score),
|
94 |
+
"is_ai_generated": bool(is_ai_generated),
|
95 |
+
"model_type": "pytorch"
|
96 |
+
}
|
97 |
+
|
98 |
+
return results
|
99 |
+
|
100 |
+
except Exception as e:
|
101 |
+
raise ValueError(f"Failed to analyze image: {str(e)}")
|
requirements.txt
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
opencv-python>=4.5.0
|
2 |
+
numpy>=1.20.0
|
3 |
+
Pillow>=8.0.0
|
4 |
+
scikit-image>=0.18.0
|
5 |
+
matplotlib>=3.3.0
|
6 |
+
torch>=1.9.0
|
7 |
+
torchvision>=0.10.0
|
8 |
+
gradio>=3.0.0
|