samuelolubukun commited on
Commit
6f0e90c
·
verified ·
1 Parent(s): 72783e3

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +29 -353
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 options
618
  st.markdown("---")
619
- st.subheader("🔍 Detailed Forgery Analysis")
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
- col1, col2 = st.columns([1, 3])
 
629
 
630
- with col1:
631
- analyze_button = st.button("Run Analysis", type="primary", use_container_width=True)
632
-
633
- with col2:
634
- st.markdown("### Analysis Guide:")
635
- if analysis_type == "Error Level Analysis (ELA)":
636
- st.info("**ELA**: Reveals compression artifacts. Bright areas indicate potential editing or manipulation.")
637
- elif analysis_type == "Copy-Move Detection":
638
- st.info("**Copy-Move**: Finds duplicated regions. Red dots show source, green dots show destination, lines show confidence.")
639
- elif analysis_type == "Tampering Detection":
640
- st.info("**Tampering**: Detects edge inconsistencies. Colored rectangles highlight suspicious regions.")
641
- elif analysis_type == "Noise Analysis":
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
- elif analysis_type == "Copy-Move Detection":
652
- analysis_buffer = create_copy_move_analysis(image)
653
- filename = "copy_move_analysis.png"
654
 
655
- elif analysis_type == "Tampering Detection":
656
- analysis_buffer = create_tampering_analysis(image)
657
- filename = "tampering_analysis.png"
 
 
 
 
 
658
 
659
- elif analysis_type == "Noise Analysis":
660
- analysis_buffer = create_noise_analysis(image)
661
- filename = "noise_analysis.png"
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'])