This guide covers the setup for Veritas, a web-based forensic tool. We are using Streamlit because it allows for rapid UI development, easy deployment, and native support for data visualization (histograms, heatmaps) without complex GUI code.
- Ensure Python 3.9+ is installed.
- Open your terminal/command prompt.
-
Create your project folder:
mkdir VeritasForensics cd VeritasForensics -
Create and activate the virtual environment:
# Windows python -m venv venv venv\Scripts\activate # Mac/Linux python3 -m venv venv source venv/bin/activate
✅ Your prompt should now show
(venv).
We are replacing PyQt6 with Streamlit. Run this command to install all necessary libraries:
pip install streamlit Pillow piexif opencv-python-headless scikit-image matplotlib numpy pandas plotlyNote: opencv-python-headless is lighter and better for server/cloud environments than standard opencv-python.
We will simplify the structure to suit a web-app workflow.
VeritasForensics/
├── venv/ # Virtual Environment
├── app.py # MAIN ENTRY POINT (Replaces main.py)
│
├── analysis/ # FORENSIC ALGORITHMS (The Brains)
│ ├── __init__.py
│ │
│ ├── ela.py # Error Level Analysis
│ ├── metadata_analysis.py # EXIF + file structure checks
│ ├── histogram_analysis.py # Statistical color/brightness features
│ ├── noise_map.py # Noise inconsistency
│ ├── jpeg_ghost.py # Multi-compression detection
│ ├── quant_table.py # JPEG quantization table forensics
│ ├── cmfd.py # Copy-move forgery detection
│ ├── prnu.py # Sensor fingerprint analysis
│ ├── frequency_analysis.py # FFT/DCT-based tampering detection
│ ├── deepfake_detector.py # GAN/deepfake artifact classifier
│ ├── resampling_detector.py # Resampling interpolation detection
│ │
│ └── util.py # Shared helpers
│
├── assets/ # Static files
│ └── style.css # Custom CSS (optional)
│
├── .streamlit/ # Streamlit Configuration
│ └── config.toml # Theme settings (Dark mode, colors)
│
├── temp/ # Temp folder for processing (ELA needs this)
│ └── .gitkeep
│
└── requirements.txt # For deployment
Create a folder named .streamlit and a file inside it named config.toml. This gives your app a professional "Forensic/Dark Mode" look immediately.
File: .streamlit/config.toml
[theme]
base="dark"
primaryColor="#00ff41"
backgroundColor="#0e1117"
secondaryBackgroundColor="#262730"
textColor="#fafafa"
font="sans serif"
[server]
headless = trueThis is your new "Main Window". It handles the sidebar, file uploading, and tabs.
File: app.py
import streamlit as st
import os
from PIL import Image
from analysis import ela, metadata_analysis
# 1. Page Configuration
st.set_page_config(
page_title="Veritas - Digital Forensics",
page_icon="🔍",
layout="wide"
)
# 2. Helper: Save Uploaded File to Disk (Required for some OpenCV algos)
def save_uploaded_file(uploaded_file):
if not os.path.exists("temp"):
os.makedirs("temp")
file_path = os.path.join("temp", uploaded_file.name)
with open(file_path, "wb") as f:
f.write(uploaded_file.getbuffer())
return file_path
# 3. Sidebar
st.sidebar.title("🔍 Veritas Tool")
st.sidebar.info("Upload a digital image to perform forensic analysis.")
uploaded_file = st.sidebar.file_uploader("Choose an Image", type=["jpg", "jpeg", "png"])
# 4. Main Logic
if uploaded_file is not None:
# Save file temporarily
file_path = save_uploaded_file(uploaded_file)
# Display Original
col1, col2 = st.columns([1, 2])
with col1:
st.image(file_path, caption="Original Image", width='stretch')
with col2:
st.warning(f"Analyzing: {uploaded_file.name}")
# 5. Analysis Tabs
tab1, tab2, tab3, tab4 = st.tabs(["🕵️ ELA", "📋 Metadata", "📊 Histogram", "👻 Noise/Ghost"])
# --- TAB 1: ELA ---
with tab1:
st.subheader("Error Level Analysis")
st.write("Highlights differences in compression levels. White areas suggest potential manipulation.")
quality = st.slider("ELA JPEG Quality", 50, 95, 90)
if st.button("Run ELA Analysis"):
with st.spinner("Processing..."):
ela_img = ela.perform_ela(file_path, quality)
st.image(ela_img, caption="ELA Result", width='stretch')
# --- TAB 2: METADATA ---
with tab2:
st.subheader("EXIF Metadata")
if st.button("Extract Metadata"):
meta = metadata_analysis.extract_metadata(file_path)
if meta:
st.json(meta)
else:
st.info("No EXIF data found.")
# --- TAB 3 & 4 (Placeholders for now) ---
with tab3:
st.info("Histogram Analysis coming soon.")
with tab4:
st.info("Noise Map & Ghost Analysis coming soon.")
else:
st.markdown("### Welcome to Veritas")
st.markdown("""
This tool allows you to analyze images for forgeries using:
* **Error Level Analysis (ELA)**
* **Metadata Extraction**
* **Noise Variance**
* **Copy-Move Detection**
""")Since we are on the web, we want functions that return Objects (Images/Dictionaries), not functions that just print to the console.
File: analysis/ela.py
import os
from PIL import Image, ImageChops, ImageEnhance
def perform_ela(image_path, quality=90):
"""
Generates an ELA image.
Returns: PIL Image object
"""
original = Image.open(image_path).convert("RGB")
# Save a temporary compressed version
temp_file = "temp/ela_temp.jpg"
original.save(temp_file, "JPEG", quality=quality)
# Open the compressed version
resaved = Image.open(temp_file)
# Calculate the difference
diff = ImageChops.difference(original, resaved)
# Enhance the difference to make it visible
extrema = diff.getextrema()
max_diff = max([ex[1] for ex in extrema])
scale = 255.0 / max_diff if max_diff != 0 else 1
ela_image = ImageEnhance.Brightness(diff).enhance(scale)
return ela_imageFile: analysis/metadata_analysis.py
import piexif
from PIL import Image
def extract_metadata(image_path):
"""
Returns: Dictionary of metadata
"""
data = {}
try:
# Load via Piexif if JPEG
exif_dict = piexif.load(image_path)
# Helper to parse bytes to string
def decode_tag(v):
if isinstance(v, bytes):
try: return v.decode('utf-8').strip('\x00')
except: return str(v)
return v
# Iterate over common IFDs
for ifd in ("0th", "Exif", "GPS", "1st"):
if ifd in exif_dict:
for tag in exif_dict[ifd]:
tag_name = piexif.TAGS[ifd][tag]["name"]
tag_value = exif_dict[ifd][tag]
data[tag_name] = decode_tag(tag_value)
except Exception:
# Fallback for PNG or non-EXIF images (basic info)
try:
img = Image.open(image_path)
data['Format'] = img.format
data['Size'] = img.size
data['Mode'] = img.mode
except:
return {"Error": "Could not read image data"}
return data- Make sure your virtual environment is active.
- Run the following command in the terminal:
streamlit run app.py
- A browser window will open automatically at
http://localhost:8501.
When you are ready to submit or show your supervisor:
- Push your code to GitHub.
- Go to share.streamlit.io.
- Log in with GitHub and click "New App".
- Select your
VeritasForensicsrepo and theapp.pyfile. - Click Deploy.
Your app is now live on the internet!