nabeelarain713 commited on
Commit
9912e68
·
verified ·
1 Parent(s): 7b0f578

flask-files

Browse files
Files changed (4) hide show
  1. Dockerfile +25 -0
  2. app.py +364 -0
  3. mask2former-ade-(splicing1_2).pth +3 -0
  4. requirements.txt +84 -0
Dockerfile ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.10-slim
2
+
3
+ # Install dependencies
4
+ RUN apt-get update && apt-get install -y git
5
+
6
+ # Install Git LFS
7
+ RUN apt-get install -y curl && \
8
+ curl -s https://packagecloud.io/install/repositories/github/git-lfs/script.deb.sh | bash && \
9
+ apt-get install -y git-lfs && \
10
+ git lfs install
11
+
12
+ # Set working directory
13
+ WORKDIR /app
14
+
15
+ # Copy files
16
+ COPY . .
17
+
18
+ # Install Python dependencies
19
+ RUN pip install --upgrade pip && pip install -r requirements.txt
20
+
21
+ # Expose port
22
+ EXPOSE 7860
23
+
24
+ # Run the Flask app
25
+ CMD ["python", "app.py"]
app.py ADDED
@@ -0,0 +1,364 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from PIL import Image
2
+ import numpy as np
3
+ import base64
4
+ from transformers import AutoImageProcessor, Mask2FormerForUniversalSegmentation
5
+ from flask import Flask, request, jsonify
6
+ from flask_cors import CORS
7
+ import matplotlib
8
+ matplotlib.use('Agg')
9
+ import matplotlib.pyplot as plt
10
+ import google.generativeai as genai
11
+ from langchain_core.messages import HumanMessage
12
+ from langchain_google_genai import ChatGoogleGenerativeAI
13
+ from reportlab.lib.utils import ImageReader
14
+ from flask import send_file, jsonify, request
15
+ from reportlab.pdfgen import canvas
16
+ from reportlab.lib.pagesizes import A4
17
+ from reportlab.lib.units import inch
18
+ import io, torch, os
19
+ from reportlab.lib import colors
20
+ from datetime import datetime
21
+
22
+ os.environ['GOOGLE_API_KEY'] = "AIzaSyCv2dNQMCD3-9s3E5Th7bDy4ko0dyucRCc"
23
+ genai.configure(api_key=os.environ['GOOGLE_API_KEY'])
24
+
25
+ # Setup
26
+ app = Flask(__name__)
27
+ CORS(app)
28
+
29
+ # Initialize device
30
+ device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
31
+
32
+ # Load model and processor
33
+ processor = AutoImageProcessor.from_pretrained("facebook/mask2former-swin-tiny-ade-semantic")
34
+ model = Mask2FormerForUniversalSegmentation.from_pretrained("facebook/mask2former-swin-tiny-ade-semantic")
35
+ # model.load_state_dict(torch.load(r"E:\FYP Work\FYP_code\backend\mask2former-ade-(splicing1_2).pth", map_location=device))
36
+ model.load_state_dict(torch.load(r"mask2former-ade-(splicing1_2).pth", map_location=device))
37
+ model = model.to(device)
38
+ model.eval()
39
+
40
+ # ========== Flask routes ==========
41
+
42
+ @app.route('/')
43
+ def home():
44
+ return "Backend is running!"
45
+
46
+ @app.route('/predict', methods=['POST'])
47
+ def predict():
48
+ if 'image' not in request.files:
49
+ return jsonify({"error": "No image uploaded"}), 400
50
+
51
+ try:
52
+ file = request.files['image']
53
+ image = Image.open(io.BytesIO(file.read()))
54
+
55
+ # Convert to RGB if needed
56
+ if image.mode != 'RGB':
57
+ image = image.convert('RGB')
58
+
59
+ # Encode original image to base64
60
+ original_image_buffer = io.BytesIO()
61
+ image.save(original_image_buffer, format="PNG")
62
+ original_image_base64 = base64.b64encode(original_image_buffer.getvalue()).decode("utf-8")
63
+
64
+ # Process image using Mask2Former processor
65
+ inputs = processor(images=image, return_tensors="pt").to(device)
66
+
67
+ # Predict
68
+ with torch.no_grad():
69
+ outputs = model(**inputs)
70
+
71
+ # Process outputs
72
+ predicted_segmentation = processor.post_process_semantic_segmentation(
73
+ outputs, target_sizes=[image.size[::-1]]
74
+ )[0]
75
+
76
+ # Convert to numpy array for visualization
77
+ segmentation_mask = predicted_segmentation.cpu().numpy()
78
+
79
+ # ========== Create visualizations ==========
80
+ # Create side-by-side plot
81
+ fig, axes = plt.subplots(1, 2, figsize=(10, 5))
82
+ axes[0].imshow(image)
83
+ axes[0].set_title("Input Image")
84
+ axes[1].imshow(segmentation_mask)
85
+ axes[1].set_title("Prediction")
86
+
87
+ for ax in axes:
88
+ ax.axis("off")
89
+ plt.tight_layout()
90
+
91
+ # Save visualization to buffer
92
+ buf = io.BytesIO()
93
+ plt.savefig(buf, format="png", bbox_inches='tight', pad_inches=0)
94
+ buf.seek(0)
95
+ visualization_base64 = base64.b64encode(buf.read()).decode('utf-8')
96
+ plt.close()
97
+
98
+ # ========== Encode mask separately ==========
99
+ # Normalize mask to 0-255 range
100
+ mask_normalized = (segmentation_mask - segmentation_mask.min()) * (255.0 / (segmentation_mask.max() - segmentation_mask.min()))
101
+ mask_image = Image.fromarray(mask_normalized.astype(np.uint8))
102
+
103
+ mask_buffer = io.BytesIO()
104
+ mask_image.save(mask_buffer, format="PNG")
105
+ mask_base64 = base64.b64encode(mask_buffer.getvalue()).decode("utf-8")
106
+
107
+
108
+ #VLM code
109
+ llm = ChatGoogleGenerativeAI(model="gemini-2.0-flash")
110
+
111
+ # Create multimodal message
112
+ message = HumanMessage(
113
+ content=[
114
+ {
115
+ "type": "text",
116
+ #"text": "Please explain briefly where the manipulation has been occured, don't use mask"
117
+ "text": " This is an image and its predicted binary mask showing manipulated regions in white. "
118
+ "Please explain briefly in 2-3 lines where the manipulation occurred and what might have been altered."
119
+ },
120
+ {
121
+ "type": "image_url",
122
+ "image_url": {
123
+ "url": f"data:image/jpeg;base64,{original_image_base64}"
124
+ },
125
+ },
126
+ {
127
+ "type": "image_url",
128
+ "image_url": {
129
+ "url": f"data:image/png;base64,{mask_base64}"
130
+ },
131
+ },
132
+ ]
133
+ )
134
+
135
+ # Get response
136
+ response = llm.invoke([message])
137
+ print(response.content)
138
+
139
+ return jsonify({
140
+ "original_image": original_image_base64,
141
+ "mask": mask_base64,
142
+ "visualization": visualization_base64,
143
+ "message": response.content
144
+ })
145
+
146
+ except Exception as e:
147
+ return jsonify({"error": str(e)}), 500
148
+
149
+ import json
150
+ from threading import Lock
151
+
152
+ counter_file = "counter.json"
153
+ counter_lock = Lock()
154
+
155
+ def get_case_id():
156
+ today = datetime.now().strftime('%Y%m%d')
157
+
158
+ with counter_lock:
159
+ if os.path.exists(counter_file):
160
+ with open(counter_file, "r") as f:
161
+ data = json.load(f)
162
+ else:
163
+ data = {}
164
+
165
+ count = data.get(today, 0) + 1
166
+ data[today] = count
167
+
168
+ with open(counter_file, "w") as f:
169
+ json.dump(data, f)
170
+
171
+ return f"DFD-{today}-{count:03d}"
172
+
173
+
174
+ @app.route('/download-report', methods=['POST'])
175
+ def download_report():
176
+ try:
177
+ file = request.files['image']
178
+ image = Image.open(io.BytesIO(file.read())).convert("RGB")
179
+
180
+ # === Process Image ===
181
+ inputs = processor(images=image, return_tensors="pt").to(device)
182
+ with torch.no_grad():
183
+ outputs = model(**inputs)
184
+ predicted_segmentation = processor.post_process_semantic_segmentation(
185
+ outputs, target_sizes=[image.size[::-1]]
186
+ )[0]
187
+ segmentation_mask = predicted_segmentation.cpu().numpy()
188
+
189
+ # === Create Mask Image ===
190
+ mask_normalized = (segmentation_mask - segmentation_mask.min()) * (255.0 / (segmentation_mask.max() - segmentation_mask.min()))
191
+ mask_image = Image.fromarray(mask_normalized.astype(np.uint8)).convert("L")
192
+
193
+ # === Prepare Images ===
194
+ image.save("temp_input.png")
195
+ mask_image.save("temp_mask.png")
196
+
197
+ # === Get LLM Analysis ===
198
+ # Encode images for LLM
199
+ original_buffer = io.BytesIO()
200
+ image.save(original_buffer, format="PNG")
201
+ original_base64 = base64.b64encode(original_buffer.getvalue()).decode("utf-8")
202
+
203
+ mask_buffer = io.BytesIO()
204
+ mask_image.save(mask_buffer, format="PNG")
205
+ mask_base64 = base64.b64encode(mask_buffer.getvalue()).decode("utf-8")
206
+
207
+ # Get professional analysis from Gemini
208
+ llm = ChatGoogleGenerativeAI(model="gemini-1.5-flash")
209
+ message = HumanMessage(
210
+ content=[
211
+ {
212
+ "type": "text",
213
+ "text": " This is an image and its predicted binary mask showing manipulated regions in white. "
214
+ "Please explain briefly where the manipulation occurred and what might have been altered."
215
+ },
216
+ {
217
+ "type": "image_url",
218
+ "image_url": {"url": f"data:image/jpeg;base64,{original_base64}"},
219
+ },
220
+ {
221
+ "type": "image_url",
222
+ "image_url": {"url": f"data:image/png;base64,{mask_base64}"},
223
+ },
224
+ ]
225
+ )
226
+ llm_response = llm.invoke([message]).content
227
+
228
+ # === Generate PDF Report ===
229
+ buffer = io.BytesIO()
230
+ c = canvas.Canvas(buffer, pagesize=A4)
231
+ width, height = A4
232
+
233
+ # === Professional Report Design ===
234
+ # Light blue background
235
+ c.setFillColorRGB(0.96, 0.96, 1)
236
+ c.rect(0, 0, width, height, fill=1, stroke=0)
237
+
238
+ # Dark blue header
239
+ c.setFillColorRGB(0, 0.2, 0.4)
240
+ c.rect(0, height-80, width, 80, fill=1, stroke=0)
241
+
242
+ # Title
243
+ c.setFillColorRGB(1, 1, 1)
244
+ c.setFont("Helvetica-Bold", 18)
245
+ c.drawCentredString(width/2, height-50, "DIGITAL IMAGE AUTHENTICITY REPORT")
246
+ c.setFont("Helvetica", 10)
247
+ c.drawCentredString(width/2, height-70, "Forensic Analysis Report")
248
+
249
+ # Metadata
250
+ c.setFillColorRGB(0, 0, 0)
251
+ c.setFont("Helvetica", 9)
252
+ c.drawString(40, height-100, f"Report Date: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
253
+ case_id = get_case_id()
254
+ c.drawString(width-200, height-100, f"Case ID: {case_id}")
255
+
256
+ # Divider
257
+ c.setStrokeColorRGB(0, 0.4, 0.6)
258
+ c.setLineWidth(1)
259
+ c.line(40, height-110, width-40, height-110)
260
+
261
+ # === Analysis Summary ===
262
+ c.setFillColorRGB(0, 0.3, 0.6)
263
+ c.setFont("Helvetica-Bold", 12)
264
+ c.drawString(40, height-140, "EXECUTIVE SUMMARY")
265
+
266
+ c.setFillColorRGB(0, 0, 0)
267
+ c.setFont("Helvetica", 10)
268
+ summary_text = [
269
+ "This report presents forensic analysis of potential digital manipulations",
270
+ "using state-of-the-art AI detection models. Key findings are summarized below."
271
+ ]
272
+ text_object = c.beginText(40, height-160)
273
+ text_object.setFont("Helvetica", 10)
274
+ text_object.setLeading(14)
275
+ for line in summary_text:
276
+ text_object.textLine(line)
277
+ c.drawText(text_object)
278
+
279
+ # === Image Evidence ===
280
+ img_y = height-420
281
+ img_width = 220
282
+ img_height = 220
283
+
284
+ # Original Image
285
+ c.drawImage("temp_input.png", 40, img_y, width=img_width, height=img_height)
286
+ c.setFillColorRGB(0, 0.3, 0.6)
287
+ c.setFont("Helvetica-Bold", 10)
288
+ c.drawString(40, img_y-20, "ORIGINAL IMAGE")
289
+
290
+ # Detection Result
291
+ c.drawImage("temp_mask.png", width-260, img_y, width=img_width, height=img_height)
292
+ c.drawString(width-260, img_y-20, "DETECTION HEATMAP")
293
+
294
+ # === AI Analysis Section ===
295
+ c.setFillColorRGB(0, 0.3, 0.6)
296
+ c.setFont("Helvetica-Bold", 12)
297
+ c.drawString(40, img_y-50, "AI FORENSIC ANALYSIS")
298
+
299
+ # Format LLM response with proper line breaks
300
+ from textwrap import wrap
301
+ analysis_lines = []
302
+ for paragraph in llm_response.split('\n'):
303
+ analysis_lines.extend(wrap(paragraph, width=90))
304
+
305
+ text_object = c.beginText(40, img_y-70)
306
+ text_object.setFont("Helvetica", 10)
307
+ text_object.setLeading(14)
308
+
309
+ # Show first 10 lines (adjust based on space)
310
+ for line in analysis_lines[:10]:
311
+ text_object.textLine(line)
312
+
313
+ if len(analysis_lines) > 10:
314
+ text_object.textLine("\n[Full analysis available in digital report]")
315
+
316
+ c.drawText(text_object)
317
+
318
+ # === Technical Details ===
319
+ c.setFillColorRGB(0, 0.3, 0.6)
320
+ c.setFont("Helvetica-Bold", 12)
321
+ c.drawString(40, img_y-180, "TECHNICAL SPECIFICATIONS")
322
+
323
+ c.setFillColorRGB(0, 0, 0)
324
+ c.setFont("Helvetica", 10)
325
+ tech_details = [
326
+ f"Analysis Model: Mask2Former-Swin (ADE20K Fine-tuned)",
327
+ #f"Detection Threshold: {segmentation_mask.max():.2f} confidence",
328
+ f"Processing Date: {datetime.now().strftime('%Y-%m-%d')}",
329
+ "Report Version: 1.1"
330
+ ]
331
+ text_object = c.beginText(40, img_y-200)
332
+ text_object.setFont("Helvetica", 10)
333
+ text_object.setLeading(14)
334
+ for line in tech_details:
335
+ text_object.textLine(line)
336
+ c.drawText(text_object)
337
+
338
+ # === Footer ===
339
+ c.setFillColorRGB(0, 0.2, 0.4)
340
+ c.rect(0, 40, width, 40, fill=1, stroke=0)
341
+ c.setFillColorRGB(1, 1, 1)
342
+ c.setFont("Helvetica", 8)
343
+ c.drawCentredString(width/2, 65, "This report was generated by AI forensic tools and should be verified by human experts")
344
+ c.drawCentredString(width/2, 55, "Sukkur IBA University | Digital Forensics Lab | © 2024 Deepfake Research Project")
345
+
346
+ c.save()
347
+ buffer.seek(0)
348
+
349
+ # Cleanup
350
+ os.remove("temp_input.png")
351
+ os.remove("temp_mask.png")
352
+
353
+ return send_file(
354
+ buffer,
355
+ mimetype='application/pdf',
356
+ as_attachment=True,
357
+ download_name=f"forensic_report_{datetime.now().strftime('%Y%m%d_%H%M')}.pdf"
358
+ )
359
+
360
+ except Exception as e:
361
+ return jsonify({"error": str(e)}), 500
362
+
363
+ if __name__ == '__main__':
364
+ app.run(host='0.0.0.0', port=5000, debug=False)
mask2former-ade-(splicing1_2).pth ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:47ac60a71770e82b8d50c45c9f867c18df7039476795c43c4986ac66c6d98f37
3
+ size 190251074
requirements.txt ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ annotated-types==0.7.0
2
+ anyio==4.9.0
3
+ blinker==1.9.0
4
+ cachetools==5.5.2
5
+ certifi==2025.4.26
6
+ chardet==5.2.0
7
+ charset-normalizer==3.4.1
8
+ click==8.1.8
9
+ colorama==0.4.6
10
+ contourpy==1.3.2
11
+ cycler==0.12.1
12
+ filelock==3.18.0
13
+ filetype==1.2.0
14
+ Flask==3.1.0
15
+ flask-cors==5.0.1
16
+ fonttools==4.57.0
17
+ fsspec==2025.3.2
18
+ google-ai-generativelanguage==0.6.15
19
+ google-api-core==2.24.2
20
+ google-api-python-client==2.169.0
21
+ google-auth==2.40.0
22
+ google-auth-httplib2==0.2.0
23
+ google-generativeai==0.8.5
24
+ googleapis-common-protos==1.70.0
25
+ greenlet==3.2.1
26
+ grpcio==1.71.0
27
+ grpcio-status==1.71.0
28
+ h11==0.16.0
29
+ httpcore==1.0.9
30
+ httplib2==0.22.0
31
+ httpx==0.28.1
32
+ huggingface-hub==0.30.2
33
+ idna==3.10
34
+ itsdangerous==2.2.0
35
+ Jinja2==3.1.6
36
+ jsonpatch==1.33
37
+ jsonpointer==3.0.0
38
+ kiwisolver==1.4.8
39
+ langchain==0.3.25
40
+ langchain-core==0.3.58
41
+ langchain-google-genai==2.1.4
42
+ langchain-text-splitters==0.3.8
43
+ langsmith==0.3.42
44
+ MarkupSafe==3.0.2
45
+ matplotlib==3.10.1
46
+ mpmath==1.3.0
47
+ networkx==3.4.2
48
+ numpy==2.2.5
49
+ orjson==3.10.18
50
+ packaging==24.2
51
+ pillow==11.2.1
52
+ proto-plus==1.26.1
53
+ protobuf==5.29.4
54
+ pyasn1==0.6.1
55
+ pyasn1_modules==0.4.2
56
+ pydantic==2.11.4
57
+ pydantic_core==2.33.2
58
+ pyparsing==3.2.3
59
+ python-dateutil==2.9.0.post0
60
+ PyYAML==6.0.2
61
+ regex==2024.11.6
62
+ reportlab==4.4.0
63
+ requests==2.32.3
64
+ requests-toolbelt==1.0.0
65
+ rsa==4.9.1
66
+ safetensors==0.5.3
67
+ scipy==1.15.2
68
+ six==1.17.0
69
+ sniffio==1.3.1
70
+ SQLAlchemy==2.0.40
71
+ sympy==1.14.0
72
+ tenacity==9.1.2
73
+ tokenizers==0.21.1
74
+ torch==2.7.0
75
+ torchaudio==2.7.0
76
+ torchvision==0.22.0
77
+ tqdm==4.67.1
78
+ transformers==4.51.3
79
+ typing-inspection==0.4.0
80
+ typing_extensions==4.13.2
81
+ uritemplate==4.1.1
82
+ urllib3==2.4.0
83
+ Werkzeug==3.1.3
84
+ zstandard==0.23.0