Update app.py
Browse files
app.py
CHANGED
|
@@ -68,143 +68,6 @@ def prepare_image_for_forgery(image):
|
|
| 68 |
ela_image = convert_to_ela_image(image, 90).resize((128, 128))
|
| 69 |
return np.array(ela_image).flatten() / 255.0
|
| 70 |
|
| 71 |
-
# Advanced Analysis Functions
|
| 72 |
-
def detect_copy_move_forgery_advanced(image):
|
| 73 |
-
"""Advanced copy-move forgery detection using multiple techniques"""
|
| 74 |
-
# Convert to grayscale
|
| 75 |
-
gray = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2GRAY)
|
| 76 |
-
|
| 77 |
-
# Parameters
|
| 78 |
-
block_size = 16
|
| 79 |
-
overlap_threshold = 8
|
| 80 |
-
correlation_threshold = 0.85
|
| 81 |
-
min_distance = 32 # Minimum distance between blocks to avoid self-matching
|
| 82 |
-
|
| 83 |
-
h, w = gray.shape
|
| 84 |
-
matches = []
|
| 85 |
-
|
| 86 |
-
# Extract overlapping blocks with their descriptors
|
| 87 |
-
blocks = []
|
| 88 |
-
positions = []
|
| 89 |
-
|
| 90 |
-
for i in range(0, h - block_size, overlap_threshold):
|
| 91 |
-
for j in range(0, w - block_size, overlap_threshold):
|
| 92 |
-
block = gray[i:i+block_size, j:j+block_size]
|
| 93 |
-
|
| 94 |
-
# Multiple feature descriptors for better matching
|
| 95 |
-
# 1. Raw block data
|
| 96 |
-
block_flat = block.flatten()
|
| 97 |
-
|
| 98 |
-
# 2. DCT coefficients (frequency domain)
|
| 99 |
-
dct_block = cv2.dct(np.float32(block))
|
| 100 |
-
dct_flat = dct_block.flatten()
|
| 101 |
-
|
| 102 |
-
# 3. LBP (Local Binary Pattern) for texture
|
| 103 |
-
lbp = feature.local_binary_pattern(block, 8, 1, method='uniform')
|
| 104 |
-
lbp_hist, _ = np.histogram(lbp.ravel(), bins=10, range=(0, 10))
|
| 105 |
-
|
| 106 |
-
# Combine features
|
| 107 |
-
descriptor = np.concatenate([
|
| 108 |
-
block_flat / 255.0, # Normalized pixel values
|
| 109 |
-
dct_flat / np.max(np.abs(dct_flat)) if np.max(np.abs(dct_flat)) > 0 else dct_flat, # Normalized DCT
|
| 110 |
-
lbp_hist / np.sum(lbp_hist) if np.sum(lbp_hist) > 0 else lbp_hist # LBP histogram
|
| 111 |
-
])
|
| 112 |
-
|
| 113 |
-
blocks.append(descriptor)
|
| 114 |
-
positions.append((j + block_size//2, i + block_size//2))
|
| 115 |
-
|
| 116 |
-
# Advanced matching using multiple similarity metrics
|
| 117 |
-
for idx1, (block1, pos1) in enumerate(zip(blocks, positions)):
|
| 118 |
-
for idx2, (block2, pos2) in enumerate(zip(blocks[idx1+1:], positions[idx1+1:]), idx1+1):
|
| 119 |
-
# Skip if blocks are too close (likely same region)
|
| 120 |
-
distance = np.sqrt((pos1[0] - pos2[0])**2 + (pos1[1] - pos2[1])**2)
|
| 121 |
-
if distance < min_distance:
|
| 122 |
-
continue
|
| 123 |
-
|
| 124 |
-
# Multiple similarity measures
|
| 125 |
-
# 1. Normalized Cross Correlation
|
| 126 |
-
correlation = np.corrcoef(block1[:block_size*block_size], block2[:block_size*block_size])[0, 1]
|
| 127 |
-
|
| 128 |
-
# 2. Structural Similarity (simplified)
|
| 129 |
-
ssim = 1 - np.mean((block1 - block2)**2) / (np.var(block1) + np.var(block2) + 1e-10)
|
| 130 |
-
|
| 131 |
-
# 3. Cosine similarity
|
| 132 |
-
cosine_sim = np.dot(block1, block2) / (np.linalg.norm(block1) * np.linalg.norm(block2) + 1e-10)
|
| 133 |
-
|
| 134 |
-
# Combined similarity score
|
| 135 |
-
combined_score = (correlation + ssim + cosine_sim) / 3
|
| 136 |
-
|
| 137 |
-
if combined_score > correlation_threshold and not np.isnan(combined_score):
|
| 138 |
-
matches.append((pos1, pos2, combined_score))
|
| 139 |
-
|
| 140 |
-
# Post-processing: Remove duplicate matches and cluster nearby matches
|
| 141 |
-
filtered_matches = []
|
| 142 |
-
for match in sorted(matches, key=lambda x: x[2], reverse=True):
|
| 143 |
-
pos1, pos2, score = match
|
| 144 |
-
|
| 145 |
-
# Check if this match is too similar to existing ones
|
| 146 |
-
is_duplicate = False
|
| 147 |
-
for existing_match in filtered_matches:
|
| 148 |
-
existing_pos1, existing_pos2, _ = existing_match
|
| 149 |
-
if (abs(pos1[0] - existing_pos1[0]) < 20 and abs(pos1[1] - existing_pos1[1]) < 20 and
|
| 150 |
-
abs(pos2[0] - existing_pos2[0]) < 20 and abs(pos2[1] - existing_pos2[1]) < 20):
|
| 151 |
-
is_duplicate = True
|
| 152 |
-
break
|
| 153 |
-
|
| 154 |
-
if not is_duplicate:
|
| 155 |
-
filtered_matches.append(match)
|
| 156 |
-
if len(filtered_matches) >= 20: # Limit to top 20 matches
|
| 157 |
-
break
|
| 158 |
-
|
| 159 |
-
return filtered_matches
|
| 160 |
-
|
| 161 |
-
def detect_splicing_regions(image):
|
| 162 |
-
"""Detect potential splicing/tampering regions using edge inconsistencies"""
|
| 163 |
-
# Convert to grayscale
|
| 164 |
-
gray = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2GRAY)
|
| 165 |
-
|
| 166 |
-
# Apply different edge detection methods
|
| 167 |
-
edges_canny = cv2.Canny(gray, 50, 150)
|
| 168 |
-
edges_sobel = cv2.Sobel(gray, cv2.CV_64F, 1, 1, ksize=3)
|
| 169 |
-
edges_sobel = np.uint8(np.absolute(edges_sobel))
|
| 170 |
-
|
| 171 |
-
# Find inconsistent regions
|
| 172 |
-
diff = cv2.absdiff(edges_canny, edges_sobel)
|
| 173 |
-
|
| 174 |
-
# Threshold and find contours
|
| 175 |
-
_, thresh = cv2.threshold(diff, 30, 255, cv2.THRESH_BINARY)
|
| 176 |
-
contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
| 177 |
-
|
| 178 |
-
# Filter contours by area
|
| 179 |
-
suspicious_regions = []
|
| 180 |
-
for contour in contours:
|
| 181 |
-
area = cv2.contourArea(contour)
|
| 182 |
-
if area > 100: # Minimum area threshold
|
| 183 |
-
x, y, w, h = cv2.boundingRect(contour)
|
| 184 |
-
suspicious_regions.append((x, y, w, h))
|
| 185 |
-
|
| 186 |
-
return suspicious_regions
|
| 187 |
-
|
| 188 |
-
def analyze_noise_patterns(image):
|
| 189 |
-
"""Analyze noise patterns to detect inconsistencies"""
|
| 190 |
-
# Convert to grayscale
|
| 191 |
-
gray = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2GRAY)
|
| 192 |
-
|
| 193 |
-
# Apply Gaussian blur and subtract to get noise
|
| 194 |
-
blurred = cv2.GaussianBlur(gray, (3, 3), 0)
|
| 195 |
-
noise = cv2.absdiff(gray, blurred)
|
| 196 |
-
|
| 197 |
-
# Divide image into blocks and calculate noise variance
|
| 198 |
-
block_size = 32
|
| 199 |
-
h, w = gray.shape
|
| 200 |
-
noise_map = np.zeros((h//block_size, w//block_size))
|
| 201 |
-
|
| 202 |
-
for i in range(0, h - block_size, block_size):
|
| 203 |
-
for j in range(0, w - block_size, block_size):
|
| 204 |
-
block = noise[i:i+block_size, j:j+block_size]
|
| 205 |
-
noise_map[i//block_size, j//block_size] = np.var(block)
|
| 206 |
-
|
| 207 |
-
return noise_map, noise
|
| 208 |
|
| 209 |
# Individual Analysis Functions
|
| 210 |
def create_ela_analysis(image):
|
|
@@ -232,132 +95,6 @@ def create_ela_analysis(image):
|
|
| 232 |
|
| 233 |
return buffer
|
| 234 |
|
| 235 |
-
def create_copy_move_analysis(image):
|
| 236 |
-
"""Create copy-move detection visualization"""
|
| 237 |
-
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 6))
|
| 238 |
-
fig.suptitle('Advanced Copy-Move Forgery Detection', fontsize=14, fontweight='bold')
|
| 239 |
-
|
| 240 |
-
# Original image
|
| 241 |
-
ax1.imshow(image)
|
| 242 |
-
ax1.set_title('Original Image')
|
| 243 |
-
ax1.axis('off')
|
| 244 |
-
|
| 245 |
-
# Copy-move detection
|
| 246 |
-
copy_move_matches = detect_copy_move_forgery_advanced(image)
|
| 247 |
-
ax2.imshow(image)
|
| 248 |
-
ax2.set_title(f'Copy-Move Detection ({len(copy_move_matches)} matches found)')
|
| 249 |
-
|
| 250 |
-
# Draw copy-move connections with confidence scores
|
| 251 |
-
colors = plt.cm.RdYlGn(np.linspace(0.3, 1, len(copy_move_matches)))
|
| 252 |
-
|
| 253 |
-
for i, (match, color) in enumerate(zip(copy_move_matches, colors)):
|
| 254 |
-
if len(match) == 3: # Advanced version with score
|
| 255 |
-
point1, point2, score = match
|
| 256 |
-
# Red dot for source
|
| 257 |
-
ax2.plot(point1[0], point1[1], 'o', color='red', markersize=5)
|
| 258 |
-
# Green dot for destination
|
| 259 |
-
ax2.plot(point2[0], point2[1], 'o', color='lime', markersize=5)
|
| 260 |
-
# Line with color based on confidence
|
| 261 |
-
ax2.plot([point1[0], point2[0]], [point1[1], point2[1]],
|
| 262 |
-
color=color, linewidth=2, alpha=0.8)
|
| 263 |
-
# Add confidence score as text
|
| 264 |
-
mid_x, mid_y = (point1[0] + point2[0]) / 2, (point1[1] + point2[1]) / 2
|
| 265 |
-
ax2.text(mid_x, mid_y, f'{score:.2f}', fontsize=8,
|
| 266 |
-
bbox=dict(boxstyle="round,pad=0.1", facecolor='white', alpha=0.7))
|
| 267 |
-
else: # Simple version
|
| 268 |
-
point1, point2 = match
|
| 269 |
-
ax2.plot(point1[0], point1[1], 'ro', markersize=4)
|
| 270 |
-
ax2.plot(point2[0], point2[1], 'go', markersize=4)
|
| 271 |
-
ax2.plot([point1[0], point2[0]], [point1[1], point2[1]], 'g-', linewidth=1, alpha=0.7)
|
| 272 |
-
|
| 273 |
-
ax2.axis('off')
|
| 274 |
-
|
| 275 |
-
plt.tight_layout()
|
| 276 |
-
|
| 277 |
-
buffer = io.BytesIO()
|
| 278 |
-
plt.savefig(buffer, format='png', dpi=150, bbox_inches='tight')
|
| 279 |
-
buffer.seek(0)
|
| 280 |
-
plt.close()
|
| 281 |
-
|
| 282 |
-
return buffer
|
| 283 |
-
|
| 284 |
-
def create_tampering_analysis(image):
|
| 285 |
-
"""Create tampering/splicing detection visualization"""
|
| 286 |
-
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 6))
|
| 287 |
-
fig.suptitle('Tampering/Splicing Detection', fontsize=14, fontweight='bold')
|
| 288 |
-
|
| 289 |
-
# Original image
|
| 290 |
-
ax1.imshow(image)
|
| 291 |
-
ax1.set_title('Original Image')
|
| 292 |
-
ax1.axis('off')
|
| 293 |
-
|
| 294 |
-
# Tampering detection
|
| 295 |
-
suspicious_regions = detect_splicing_regions(image)
|
| 296 |
-
ax2.imshow(image)
|
| 297 |
-
ax2.set_title(f'Suspicious Regions ({len(suspicious_regions)} found)')
|
| 298 |
-
|
| 299 |
-
# Highlight suspicious regions with different colors
|
| 300 |
-
colors = ['red', 'orange', 'yellow', 'purple', 'pink']
|
| 301 |
-
for i, region in enumerate(suspicious_regions):
|
| 302 |
-
x, y, w, h = region
|
| 303 |
-
color = colors[i % len(colors)]
|
| 304 |
-
rect = patches.Rectangle((x, y), w, h, linewidth=2,
|
| 305 |
-
edgecolor=color, facecolor='none', alpha=0.8)
|
| 306 |
-
ax2.add_patch(rect)
|
| 307 |
-
# Add region number
|
| 308 |
-
ax2.text(x, y-5, f'R{i+1}', fontsize=10, color=color, fontweight='bold')
|
| 309 |
-
|
| 310 |
-
ax2.axis('off')
|
| 311 |
-
|
| 312 |
-
plt.tight_layout()
|
| 313 |
-
|
| 314 |
-
buffer = io.BytesIO()
|
| 315 |
-
plt.savefig(buffer, format='png', dpi=150, bbox_inches='tight')
|
| 316 |
-
buffer.seek(0)
|
| 317 |
-
plt.close()
|
| 318 |
-
|
| 319 |
-
return buffer
|
| 320 |
-
|
| 321 |
-
def create_noise_analysis(image):
|
| 322 |
-
"""Create noise pattern analysis visualization"""
|
| 323 |
-
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(12, 10))
|
| 324 |
-
fig.suptitle('Noise Pattern Analysis', fontsize=14, fontweight='bold')
|
| 325 |
-
|
| 326 |
-
# Original image
|
| 327 |
-
ax1.imshow(image)
|
| 328 |
-
ax1.set_title('Original Image')
|
| 329 |
-
ax1.axis('off')
|
| 330 |
-
|
| 331 |
-
# Noise analysis
|
| 332 |
-
noise_map, noise = analyze_noise_patterns(image)
|
| 333 |
-
|
| 334 |
-
# Noise map
|
| 335 |
-
im1 = ax2.imshow(noise_map, cmap='hot', interpolation='nearest')
|
| 336 |
-
ax2.set_title('Noise Variance Map')
|
| 337 |
-
ax2.axis('off')
|
| 338 |
-
plt.colorbar(im1, ax=ax2, shrink=0.8)
|
| 339 |
-
|
| 340 |
-
# Raw noise
|
| 341 |
-
ax3.imshow(noise, cmap='gray')
|
| 342 |
-
ax3.set_title('Extracted Noise')
|
| 343 |
-
ax3.axis('off')
|
| 344 |
-
|
| 345 |
-
# Noise histogram
|
| 346 |
-
ax4.hist(noise.flatten(), bins=50, alpha=0.7, color='blue')
|
| 347 |
-
ax4.set_title('Noise Distribution')
|
| 348 |
-
ax4.set_xlabel('Noise Level')
|
| 349 |
-
ax4.set_ylabel('Frequency')
|
| 350 |
-
ax4.grid(True, alpha=0.3)
|
| 351 |
-
|
| 352 |
-
plt.tight_layout()
|
| 353 |
-
|
| 354 |
-
buffer = io.BytesIO()
|
| 355 |
-
plt.savefig(buffer, format='png', dpi=150, bbox_inches='tight')
|
| 356 |
-
buffer.seek(0)
|
| 357 |
-
plt.close()
|
| 358 |
-
|
| 359 |
-
return buffer
|
| 360 |
-
|
| 361 |
|
| 362 |
# Deepfake Image Detection
|
| 363 |
def predict_deepfake_image(image_path, model):
|
|
@@ -614,103 +351,42 @@ if task == "Image Forgery Detection":
|
|
| 614 |
else:
|
| 615 |
st.error(f"Result: Forged Image with {confidence_fake:.2f}% confidence")
|
| 616 |
|
| 617 |
-
# Add analysis
|
| 618 |
st.markdown("---")
|
| 619 |
-
st.subheader("🔍
|
| 620 |
-
|
| 621 |
-
# Analysis type selection
|
| 622 |
-
analysis_type = st.selectbox(
|
| 623 |
-
"Choose Analysis Type:",
|
| 624 |
-
["Error Level Analysis (ELA)", "Copy-Move Detection", "Tampering Detection", "Noise Analysis"],
|
| 625 |
-
index=0
|
| 626 |
-
)
|
| 627 |
|
| 628 |
-
|
|
|
|
| 629 |
|
| 630 |
-
|
| 631 |
-
|
| 632 |
-
|
| 633 |
-
|
| 634 |
-
st.
|
| 635 |
-
|
| 636 |
-
|
| 637 |
-
|
| 638 |
-
|
| 639 |
-
|
| 640 |
-
st.
|
| 641 |
-
|
| 642 |
-
st.info("**Noise**: Analyzes noise patterns. Hot spots in variance map indicate inconsistent noise.")
|
| 643 |
-
|
| 644 |
-
if analyze_button:
|
| 645 |
-
with st.spinner(f"Running {analysis_type}..."):
|
| 646 |
-
try:
|
| 647 |
-
if analysis_type == "Error Level Analysis (ELA)":
|
| 648 |
analysis_buffer = create_ela_analysis(image)
|
| 649 |
-
filename = "ela_analysis.png"
|
| 650 |
|
| 651 |
-
|
| 652 |
-
analysis_buffer =
|
| 653 |
-
filename = "copy_move_analysis.png"
|
| 654 |
|
| 655 |
-
|
| 656 |
-
|
| 657 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 658 |
|
| 659 |
-
|
| 660 |
-
|
| 661 |
-
|
| 662 |
-
|
| 663 |
-
# Fixed size for analysis results
|
| 664 |
-
st.image(analysis_buffer, caption=f"{analysis_type} Results", width=500)
|
| 665 |
-
|
| 666 |
-
# Detailed results based on analysis type
|
| 667 |
-
if analysis_type == "Copy-Move Detection":
|
| 668 |
-
matches = detect_copy_move_forgery_advanced(image)
|
| 669 |
-
if matches:
|
| 670 |
-
st.success(f"Found {len(matches)} potential copy-move regions")
|
| 671 |
-
with st.expander("Detailed Match Information"):
|
| 672 |
-
for i, match in enumerate(matches[:5]): # Show top 5
|
| 673 |
-
if len(match) == 3:
|
| 674 |
-
pos1, pos2, score = match
|
| 675 |
-
st.write(f"**Match {i+1}**: Confidence {score:.3f}")
|
| 676 |
-
st.write(f" Source: ({pos1[0]}, {pos1[1]}) → Destination: ({pos2[0]}, {pos2[1]})")
|
| 677 |
-
else:
|
| 678 |
-
st.success("No copy-move forgery detected")
|
| 679 |
-
|
| 680 |
-
elif analysis_type == "Tampering Detection":
|
| 681 |
-
regions = detect_splicing_regions(image)
|
| 682 |
-
if regions:
|
| 683 |
-
st.warning(f"Found {len(regions)} suspicious regions")
|
| 684 |
-
with st.expander("Region Details"):
|
| 685 |
-
for i, (x, y, w, h) in enumerate(regions):
|
| 686 |
-
st.write(f"**Region {i+1}**: Position ({x}, {y}), Size {w}×{h}")
|
| 687 |
-
else:
|
| 688 |
-
st.success("No suspicious tampering regions detected")
|
| 689 |
-
|
| 690 |
-
elif analysis_type == "Noise Analysis":
|
| 691 |
-
noise_map, _ = analyze_noise_patterns(image)
|
| 692 |
-
avg_noise = np.mean(noise_map)
|
| 693 |
-
std_noise = np.std(noise_map)
|
| 694 |
-
st.info(f"Average noise variance: {avg_noise:.3f}")
|
| 695 |
-
st.info(f"Noise variance std: {std_noise:.3f}")
|
| 696 |
-
if std_noise > avg_noise * 0.5:
|
| 697 |
-
st.warning("High noise variance detected - possible inconsistent editing")
|
| 698 |
-
else:
|
| 699 |
-
st.success("Noise patterns appear consistent")
|
| 700 |
-
|
| 701 |
-
# Download button
|
| 702 |
-
st.download_button(
|
| 703 |
-
label=f"Download {analysis_type} Results",
|
| 704 |
-
data=analysis_buffer.getvalue(),
|
| 705 |
-
file_name=filename,
|
| 706 |
-
mime="image/png",
|
| 707 |
-
use_container_width=True
|
| 708 |
-
)
|
| 709 |
-
|
| 710 |
-
except Exception as e:
|
| 711 |
-
st.error(f"Error during {analysis_type}: {str(e)}")
|
| 712 |
-
st.info("Some analysis methods may not work with all image types. Try with a different image if needed.")
|
| 713 |
-
|
| 714 |
|
| 715 |
elif task == "Deepfake Image Detection":
|
| 716 |
uploaded_file = st.file_uploader("Upload an image", type=['jpg', 'jpeg', 'png'])
|
|
|
|
| 68 |
ela_image = convert_to_ela_image(image, 90).resize((128, 128))
|
| 69 |
return np.array(ela_image).flatten() / 255.0
|
| 70 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 71 |
|
| 72 |
# Individual Analysis Functions
|
| 73 |
def create_ela_analysis(image):
|
|
|
|
| 95 |
|
| 96 |
return buffer
|
| 97 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 98 |
|
| 99 |
# Deepfake Image Detection
|
| 100 |
def predict_deepfake_image(image_path, model):
|
|
|
|
| 351 |
else:
|
| 352 |
st.error(f"Result: Forged Image with {confidence_fake:.2f}% confidence")
|
| 353 |
|
| 354 |
+
# Add ELA analysis option
|
| 355 |
st.markdown("---")
|
| 356 |
+
st.subheader("🔍 Additional Analysis")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 357 |
|
| 358 |
+
# Show ELA option checkbox
|
| 359 |
+
show_ela = st.checkbox("View Error Level Analysis (ELA)", value=False)
|
| 360 |
|
| 361 |
+
if show_ela:
|
| 362 |
+
st.markdown("### Error Level Analysis")
|
| 363 |
+
st.info("**ELA**: Reveals compression artifacts. Bright areas indicate potential editing or manipulation.")
|
| 364 |
+
|
| 365 |
+
col1, col2 = st.columns([1, 3])
|
| 366 |
+
|
| 367 |
+
with col1:
|
| 368 |
+
analyze_button = st.button("Run ELA Analysis", type="primary", use_container_width=True)
|
| 369 |
+
|
| 370 |
+
if analyze_button:
|
| 371 |
+
with st.spinner("Running Error Level Analysis..."):
|
| 372 |
+
try:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 373 |
analysis_buffer = create_ela_analysis(image)
|
|
|
|
| 374 |
|
| 375 |
+
# Fixed size for analysis results
|
| 376 |
+
st.image(analysis_buffer, caption="ELA Analysis Results", width=500)
|
|
|
|
| 377 |
|
| 378 |
+
# Download button
|
| 379 |
+
st.download_button(
|
| 380 |
+
label="Download ELA Results",
|
| 381 |
+
data=analysis_buffer.getvalue(),
|
| 382 |
+
file_name="ela_analysis.png",
|
| 383 |
+
mime="image/png",
|
| 384 |
+
use_container_width=True
|
| 385 |
+
)
|
| 386 |
|
| 387 |
+
except Exception as e:
|
| 388 |
+
st.error(f"Error during ELA analysis: {str(e)}")
|
| 389 |
+
st.info("ELA analysis may not work with all image types. Try with a different image if needed.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 390 |
|
| 391 |
elif task == "Deepfake Image Detection":
|
| 392 |
uploaded_file = st.file_uploader("Upload an image", type=['jpg', 'jpeg', 'png'])
|