Deepfake image detection sounds like it should be simple.
Upload image. Run model. Get answer.
Fake: 97%
Very clean. Very satisfying.
Also… very dangerous if we trust it too much.
Deepfake detection is a moving target. Image generators keep improving. Social platforms compress images. People crop screenshots. Metadata gets stripped. Real photos get edited. Fake images get upscaled. Some deepfakes are face swaps. Some are fully AI-generated portraits. Some are ordinary real photos with one manipulated part. And some images are not deepfakes at all — they are just weirdly lit, heavily filtered, or compressed into pixel soup.
So the safest way to build deepfake image detection in Python is not:
This image is fake.
It is:
This image has signals that deserve review.
In this guide, we’ll build a practical Python workflow for detecting deepfake images. We’ll use metadata checks, image-quality signals, face-aware preprocessing, pretrained image classifiers, batch scanning, and a final risk report.
The goal is not courtroom-level proof.
The goal is a useful review pipeline.
What does “deepfake image detection” mean?
Deepfake image detection means estimating whether an image may have been generated or manipulated by AI.
That can include:
| Type | Example |
| Fully AI-generated image | A fake person generated from scratch |
| Face swap | One person’s face placed onto another body |
| Face morph | A face blended from two identities |
| Attribute edit | Age, hair, expression, or identity changed |
| Inpainting | Part of the image added or replaced |
| AI upscaling/restoration | Real image modified by AI tools |
| Screenshot of AI image | Generated image reposted as a screenshot |
| Compressed AI image | Fake image heavily compressed by social media |
Those are different problems.
A classifier trained on fully generated images may not catch a subtle face swap. A face-swap detector may not catch AI-generated backgrounds. A metadata check may help with original camera photos but fail on screenshots. That is why a real detector should combine signals instead of trusting one clue.
Why deepfake detection is hard
Deepfake detection is hard because the fake-image world changes constantly.
A detector trained on old GAN artifacts may fail on newer diffusion models. A model trained on clean benchmark images may fail after JPEG compression, screenshots, resizing, or social media processing. And even strong detectors can overfit to dataset quirks instead of learning stable forensic signals.
Recent research keeps pointing to this problem. The 2025 RedFace paper argues that many academic deepfake benchmarks lack real-world diversity, so detectors can look good in the lab but struggle against “in-the-wild” forgery styles. The authors built a dataset with more than 60,000 forged images and 1,000 manipulated videos from real-world-oriented deepfake platforms to test that gap.
A 2026 paper on continuously evolving deepfake detection makes the same warning even louder: detectors with near-perfect scores on academic benchmarks can collapse on real-world content, with reported AUC drops of 45-50% for some open-source models under in-the-wild conditions.
So our Python detector should be humble.
It should produce:
- A risk score.
- Supporting signals.
- Review recommendations.
- Warnings about uncertainty.
Not one dramatic final verdict.
Why we can write this guide
We’ve spent around 6 years working with AI APIs, computer vision workflows, image embeddings, OCR, content moderation, media analysis, and developer tutorials. We also checked current docs and recent research on deepfake detection, image classification, face morph detection, and real-world fake image benchmarks.
The practical lesson is simple: deepfake detection is a layered workflow.
Use a model, yes. But also check metadata, compression, image quality, face crops, suspicious artifacts, provenance signals, and review logic. And if the result affects safety, identity, fraud, moderation, journalism, legal review, or someone’s reputation, keep a human in the loop.
What we’ll build
We’ll build a Python workflow with five layers:
- Metadata inspection
Check EXIF data, software tags, missing camera info, and suspicious editing traces. - Image quality checks
Look at blur, resolution, compression, and whether the image is too degraded for reliable analysis. - Face-aware preprocessing
Detect and crop faces so the classifier can focus on the manipulated region. - Pretrained classifier inference
Use a Hugging Face image classification model or your own trained model. - Risk report
Combine signals into a structured JSON result.
The final output will look like this:
{
"image_path": "sample.jpg",
"risk_label": "needs_review",
"deepfake_risk_score": 0.78,
"model_score": 0.84,
"metadata_score": 0.35,
"quality_warnings": [
"Image is heavily compressed",
"EXIF camera metadata is missing"
],
"recommendation": "Send this image to manual review.",
"warning": "This result is probabilistic and should not be treated as proof."
}
That is much safer than “fake” or “real.”
Step 1: Install the Python packages
Start with the core packages.
pip install pillow opencv-python numpy pandas transformers torch torchvision
Optional but useful:
pip install mediapipe scikit-image
We’ll use:
| Package | Why |
| Pillow | Open images and inspect EXIF metadata |
| OpenCV | Image preprocessing, blur checks, face detection |
| NumPy | Image arrays and scoring |
| Pandas | Batch reports |
| Transformers | Run pretrained image classifiers |
| Torch | Model inference |
| MediaPipe | Face detection/cropping option |
| scikit-image | Optional similarity/image metrics |
Hugging Face’s Transformers docs include an ImageClassificationPipeline, which is the simplest way to run image classification models from Python. Pillow also exposes Image.getexif() for reading EXIF data from images.
Step 2: Load and normalize an image
Let’s start with safe image loading.
from PIL import Image
from pathlib import Path
def load_image(image_path):
image_path = Path(image_path)
if not image_path.exists():
raise FileNotFoundError(f"Image not found: {image_path}")
image = Image.open(image_path).convert("RGB")
return image
image = load_image("sample.jpg")
print(image.size)
This converts the image to RGB so your model receives a predictable format.
Step 3: Inspect EXIF metadata
Metadata alone cannot prove whether an image is real or fake.
But it can provide useful clues.
A camera photo may include tags like:
- Camera make.
- Camera model.
- Date taken.
- Lens info.
- GPS data.
- Exposure settings.
- Editing software.
AI-generated images, screenshots, downloaded social media images, and edited images often have missing or stripped metadata. But missing metadata is not proof of fakery. Many apps remove metadata for privacy.
Let’s inspect it.
from PIL import Image, ExifTags
def extract_exif(image_path):
image = Image.open(image_path)
exif = image.getexif()
readable = {}
for tag_id, value in exif.items():
tag_name = ExifTags.TAGS.get(tag_id, tag_id)
readable[str(tag_name)] = str(value)
return readable
metadata = extract_exif("sample.jpg")
for key, value in metadata.items():
print(key, ":", value)
Now create a simple metadata risk checker.
SUSPICIOUS_SOFTWARE_TERMS = [
"photoshop",
"stable diffusion",
"midjourney",
"dall",
"comfyui",
"automatic1111",
"firefly",
"generative",
"ai"
]
def score_metadata(metadata):
score = 0.0
signals = []
if not metadata:
score += 0.25
signals.append("No EXIF metadata found.")
camera_fields = ["Make", "Model", "LensModel", "DateTimeOriginal"]
missing_camera_fields = [
field for field in camera_fields
if field not in metadata
]
if len(missing_camera_fields) >= 3:
score += 0.15
signals.append("Most camera metadata fields are missing.")
software = metadata.get("Software", "").lower()
if software:
for term in SUSPICIOUS_SOFTWARE_TERMS:
if term in software:
score += 0.35
signals.append(f"Software metadata mentions: {term}")
break
score = min(score, 1.0)
return {
"metadata_score": round(score, 2),
"metadata_signals": signals
}
Use it:
metadata_report = score_metadata(metadata)
print(metadata_report)
Again: metadata is only one signal.
A real image can have no EXIF. A fake image can have fake EXIF. Treat this as context, not proof.
Step 4: Check image quality before detection
A deepfake detector can fail if the image is too blurry, tiny, compressed, or edited.
So before running a classifier, check whether the image is suitable.
Let’s use OpenCV to calculate blur with the variance of the Laplacian. This is a common focus/blur heuristic in image processing.
import cv2
import numpy as np
from PIL import Image
def calculate_blur_score(image_path):
image = cv2.imread(str(image_path))
if image is None:
raise ValueError(f"Could not read image: {image_path}")
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blur_score = cv2.Laplacian(gray, cv2.CV_64F).var()
return float(blur_score)
blur_score = calculate_blur_score("sample.jpg")
print("Blur score:", blur_score)
Lower scores usually mean blurrier images.
Now wrap it:
def image_quality_report(image_path):
image = Image.open(image_path)
width, height = image.size
blur_score = calculate_blur_score(image_path)
warnings = []
if width < 256 or height < 256:
warnings.append("Image resolution is low.")
if blur_score < 80:
warnings.append("Image may be blurry, which can reduce detection reliability.")
megapixels = (width * height) / 1_000_000
return {
"width": width,
"height": height,
"megapixels": round(megapixels, 2),
"blur_score": round(blur_score, 2),
"quality_warnings": warnings
}
Use it:
quality = image_quality_report("sample.jpg")
print(quality)
A 2026 fake image detection comparison paper found that even strong CNN baselines can suffer from imbalance, overfitting, and limited cross-domain robustness. That is one reason quality warnings matter: you need to know when the input image is not a fair test for the detector.
Step 5: Detect faces and crop them
Many deepfake images target faces.
So if your goal is facial deepfake detection, crop the face first.
This helps because the classifier can focus on the face instead of the background.
We’ll use OpenCV’s built-in Haar cascade for a simple demo. It is not the best face detector in the world, but it is easy to run.
import cv2
from pathlib import Path
def detect_face_crops(image_path, output_dir="face_crops"):
output_dir = Path(output_dir)
output_dir.mkdir(exist_ok=True)
image = cv2.imread(str(image_path))
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
cascade_path = cv2.data.haarcascades + "haarcascade_frontalface_default.xml"
face_cascade = cv2.CascadeClassifier(cascade_path)
faces = face_cascade.detectMultiScale(
gray,
scaleFactor=1.1,
minNeighbors=5,
minSize=(80, 80)
)
crop_paths = []
for index, (x, y, w, h) in enumerate(faces):
padding = int(0.25 * max(w, h))
x1 = max(x - padding, 0)
y1 = max(y - padding, 0)
x2 = min(x + w + padding, image.shape[1])
y2 = min(y + h + padding, image.shape[0])
crop = image[y1:y2, x1:x2]
crop_path = output_dir / f"{Path(image_path).stem}_face_{index}.jpg"
cv2.imwrite(str(crop_path), crop)
crop_paths.append(str(crop_path))
return crop_paths
face_crops = detect_face_crops("sample.jpg")
print(face_crops)
For production, use a better face detector, especially if you expect profile faces, multiple people, low light, occlusion, or small faces.
Step 6: Run a pretrained deepfake image classifier
Now we’ll use a pretrained image classification model.
The exact model you choose matters a lot. Check the model card, training data, license, intended use, and limitations before using it in production.
Use Hugging Face’s image classification pipeline:
from transformers import pipeline
classifier = pipeline(
task="image-classification",
model="your-deepfake-image-detection-model"
)
result = classifier("sample.jpg")
print(result)
The output usually looks like:
[
{
"label": "fake",
"score": 0.87
},
{
"label": "real",
"score": 0.13
}
]
Because model labels vary, add a normalizer.
FAKE_LABEL_KEYWORDS = [
"fake",
"deepfake",
"ai",
"generated",
"synthetic",
"manipulated"
]
REAL_LABEL_KEYWORDS = [
"real",
"authentic",
"natural",
"human"
]
def normalize_classifier_output(results):
fake_score = 0.0
real_score = 0.0
for item in results:
label = item["label"].lower()
score = float(item["score"])
if any(keyword in label for keyword in FAKE_LABEL_KEYWORDS):
fake_score = max(fake_score, score)
if any(keyword in label for keyword in REAL_LABEL_KEYWORDS):
real_score = max(real_score, score)
if fake_score == 0.0 and real_score == 0.0:
return {
"model_score": None,
"model_label": "unknown",
"warning": "Could not map model labels to fake/real categories."
}
if fake_score >= real_score:
label = "ai_or_manipulated_risk"
score = fake_score
else:
label = "low_model_risk"
score = 1 - real_score
return {
"model_score": round(score, 4),
"model_label": label
}
Use it:
raw_result = classifier("sample.jpg")
model_report = normalize_classifier_output(raw_result)
print(model_report)
Do not skip model-card review. Some models detect AI-generated images. Some detect face swaps. Some only work on cropped faces. Some were trained on old generators. Some may be biased toward certain compression artifacts.
Step 7: Run the model on face crops
If the image has faces, run the model on each crop too.
def analyze_face_crops(face_crops, classifier):
face_reports = []
for crop_path in face_crops:
raw_result = classifier(crop_path)
normalized = normalize_classifier_output(raw_result)
face_reports.append({
"crop_path": crop_path,
"raw_result": raw_result,
"normalized": normalized
})
return face_reports
face_reports = analyze_face_crops(face_crops, classifier)
for report in face_reports:
print(report)
Then use the highest face risk score.
def max_face_risk(face_reports):
scores = []
for report in face_reports:
score = report["normalized"].get("model_score")
if score is not None:
scores.append(score)
if not scores:
return None
return max(scores)
This matters because a full image may look mostly normal while the manipulated part is only one face.
Step 8: Combine signals into a risk score
Now let’s combine:
- Metadata score.
- Full-image model score.
- Face-crop model score.
- Quality warnings.
We will weight model outputs more heavily than metadata.
def combine_deepfake_signals(
model_score=None,
face_score=None,
metadata_score=0.0,
quality_warnings=None
):
quality_warnings = quality_warnings or []
available_scores = []
if model_score is not None:
available_scores.append(("model", model_score, 0.55))
if face_score is not None:
available_scores.append(("face", face_score, 0.30))
available_scores.append(("metadata", metadata_score, 0.15))
weighted_sum = 0.0
total_weight = 0.0
for _, score, weight in available_scores:
weighted_sum += score * weight
total_weight += weight
risk_score = weighted_sum / total_weight if total_weight else 0.0
if len(quality_warnings) >= 2:
# Do not necessarily increase fake risk too much.
# Instead, lower confidence and push to review.
risk_score = max(risk_score, 0.45)
if risk_score >= 0.75:
risk_label = "needs_review_high"
recommendation = "Send this image to manual review before making a decision."
elif risk_score >= 0.45:
risk_label = "unclear_review_recommended"
recommendation = "Review recommended. Signals are mixed or image quality is limited."
else:
risk_label = "low_detected_risk"
recommendation = "No strong deepfake signal found, but this is not proof of authenticity."
return {
"deepfake_risk_score": round(risk_score, 2),
"risk_label": risk_label,
"recommendation": recommendation,
"warning": "Deepfake detection is probabilistic and can produce false positives and false negatives."
}
This is intentionally conservative.
Bad image quality does not automatically mean fake. It means less reliable.
Step 9: Build one full detector function
Now let’s put everything together.
def detect_deepfake_image(image_path, classifier):
metadata = extract_exif(image_path)
metadata_report = score_metadata(metadata)
quality = image_quality_report(image_path)
raw_result = classifier(image_path)
model_report = normalize_classifier_output(raw_result)
face_crops = detect_face_crops(image_path)
face_reports = analyze_face_crops(face_crops, classifier) if face_crops else []
face_score = max_face_risk(face_reports)
model_score = model_report.get("model_score")
combined = combine_deepfake_signals(
model_score=model_score,
face_score=face_score,
metadata_score=metadata_report["metadata_score"],
quality_warnings=quality["quality_warnings"]
)
return {
"image_path": str(image_path),
"risk": combined,
"model": {
"raw_result": raw_result,
"normalized": model_report
},
"faces": {
"face_count": len(face_crops),
"max_face_score": face_score,
"reports": face_reports
},
"metadata": metadata_report,
"quality": quality
}
Use it:
report = detect_deepfake_image("sample.jpg", classifier)
print(report)
For nicer output:
import json
print(json.dumps(report, indent=2))
Now you have a structured deepfake image detection report.
Step 10: Batch-check a folder of images
For moderation, fraud review, or media verification, you may need to scan many images.
from pathlib import Path
import pandas as pd
def batch_detect_images(folder_path, classifier, output_csv="deepfake_report.csv"):
folder_path = Path(folder_path)
image_paths = [
path for path in folder_path.iterdir()
if path.suffix.lower() in [".jpg", ".jpeg", ".png", ".webp"]
]
rows = []
for image_path in image_paths:
try:
report = detect_deepfake_image(image_path, classifier)
rows.append({
"image_path": str(image_path),
"risk_label": report["risk"]["risk_label"],
"deepfake_risk_score": report["risk"]["deepfake_risk_score"],
"model_score": report["model"]["normalized"].get("model_score"),
"face_count": report["faces"]["face_count"],
"max_face_score": report["faces"]["max_face_score"],
"metadata_score": report["metadata"]["metadata_score"],
"blur_score": report["quality"]["blur_score"],
"quality_warnings": "; ".join(report["quality"]["quality_warnings"]),
"recommendation": report["risk"]["recommendation"]
})
except Exception as error:
rows.append({
"image_path": str(image_path),
"risk_label": "error",
"deepfake_risk_score": None,
"error": str(error)
})
df = pd.DataFrame(rows)
df.to_csv(output_csv, index=False)
return df
df = batch_detect_images("images", classifier)
print(df.head())
Now you have a CSV review queue.
This is often more useful than a single-image script.
Step 11: Build a small FastAPI endpoint
If you want to use the detector in an app, wrap it in an API.
Install:
pip install fastapi uvicorn python-multipart
Create app.py:
import tempfile
from fastapi import FastAPI, UploadFile, File
from transformers import pipeline
app = FastAPI()
classifier = pipeline(
task="image-classification",
model="your-deepfake-image-detection-model"
)
@app.post("/detect-deepfake")
async def detect_deepfake(file: UploadFile = File(...)):
suffix = "." + file.filename.split(".")[-1].lower()
with tempfile.NamedTemporaryFile(suffix=suffix, delete=True) as temp_file:
temp_file.write(await file.read())
temp_file.flush()
report = detect_deepfake_image(temp_file.name, classifier)
return report
Run:
uvicorn app:app --reload
Test:
curl -X POST "http://127.0.0.1:8000/detect-deepfake" \
-F "[email protected]"
This gives you a basic deepfake detection API.
For production, add:
- File type validation.
- Max file size.
- Malware scanning.
- Authentication.
- Rate limits.
- Logging.
- Human review queue.
- Model version tracking.
- Secure storage rules.
- Privacy policy.
What about face morph detection?
Face morph detection is a related problem, especially for identity documents.
A face morph blends two or more identities into one image. This can be used for identity fraud because the resulting image may match multiple people.
NIST has ongoing Face Analysis Technology Evaluation work around morph detection. In May 2026, NIST published an updated FATE MORPH report and tracks morphing attack classification error rates across submitted algorithms and datasets. NIST also published operational guidance in 2025 explaining that organizations should think about what happens after a morph detector flags a potentially fake photo, not only whether the detector raises a score.
That matters because identity workflows are high-stakes.
If you build for identity verification, do not rely on a random open-source deepfake classifier. Use specialized morph/liveness/document verification systems, proper evaluation, and human review.
What about provenance and watermarks?
Deepfake detection guesses from image signals.
Provenance tries to verify where the image came from.
Those are different.
Watermarks, Content Credentials, camera signing, and media provenance systems can be stronger than style-based detection when the signal is available. But they are not universal. Content can be cropped, screenshotted, recompressed, stripped of metadata, or created by tools that do not include provenance data.
Recent reporting around Meta, Google SynthID, and AI image watermarking shows that major platforms are investing in hidden provenance signals, but also that detection coverage is still incomplete, especially for older generated content or media created outside a provider’s own system.
So a practical workflow can combine:
| Signal | What it adds |
| Deepfake classifier | Visual/manipulation risk |
| EXIF metadata | Camera/editing context |
| Provenance credentials | Stronger authenticity signal when present |
| Reverse image search | Where else image appeared |
| Face/source comparison | Identity consistency |
| Human review | Final judgment for sensitive cases |
Do not expect one signal to solve everything.
How to improve detector quality
A basic pretrained classifier is only the start.
To improve quality:
- Use a model trained for your exact problem.
- Crop faces if detecting facial deepfakes.
- Train or fine-tune on real examples from your workflow.
- Include compressed, cropped, upscaled, and screenshotted images.
- Include real edited images as negative examples.
- Track false positives and false negatives.
- Use separate thresholds for different risk levels.
- Keep model versions and evaluation sets.
- Re-test when new generators appear.
- Add manual review for high-risk outputs.
The 2026 continuously evolving deepfake detection paper argues that static detectors are structurally disadvantaged because generators keep changing. That is a good product lesson: your detector cannot be “set and forget.”
How to evaluate your detector
Do not test on 10 obvious fake images.
Build a real evaluation set.
Include:
- Real camera images.
- AI-generated portraits.
- Face swaps.
- Face morphs.
- Edited real photos.
- Screenshots of generated images.
- JPEG-compressed images.
- Cropped images.
- Upscaled images.
- Social media downloads.
- Low-light images.
- Multiple ethnicities, ages, genders, and lighting conditions.
- Negative examples that look suspicious but are real.
Track:
| Metric | Why it matters |
| Accuracy | Overall correct predictions |
| Precision | How many flagged images are really fake |
| Recall | How many fake images are caught |
| False positive rate | Real images wrongly flagged |
| False negative rate | Fake images missed |
| AUC | Ranking quality across thresholds |
| Calibration | Whether scores match real risk |
| Robustness | Performance after compression/cropping |
| Bias/fairness | Whether groups are flagged differently |
| Review workload | How many images need humans |
In many moderation or fraud systems, false positives and false negatives have different costs.
If you are reviewing platform content, a false positive may unfairly block a real user. If you are screening identity fraud, a false negative may let a fake identity through.
Pick thresholds based on the workflow, not vibes.
What should your UI say?
Please do not show users:
This image is fake.
Use safer labels:
Low detected risk
Unclear
Needs review
High-risk signal detected
Image quality too low for reliable analysis
A better UI shows:
| UI field | Example |
| Risk label | Needs review |
| Risk score | 78% |
| Signals | Model score high, EXIF missing, face crop suspicious |
| Quality warning | Image is low resolution |
| Model version | detector_v3.2 |
| Recommendation | Send to manual review |
| Warning | This is not proof of manipulation |
This is more honest and more useful.
Where LLMAPI fits
LLMAPI fits after the visual detector, when your app needs summaries, review notes, routing, or human-readable explanations.
The deepfake detector returns structured signals:
{
“risk_label”: “needs_review_high”,
“deepfake_risk_score”: 0.78,
“quality_warnings”: [“Image is low resolution”],
“metadata_signals”: [“No EXIF metadata found”]
}
LLMAPI can help turn that into workflow output:
| Task | Example |
| Review summary | “This image needs review because model and metadata signals are suspicious.” |
| Moderation note | Draft an internal reviewer explanation |
| Case routing | Send high-risk identity images to fraud review |
| User-facing wording | Write a careful non-accusatory message |
| Batch report | Summarize weekly fake-image trends |
| Evidence checklist | List what a reviewer should verify next |
| Policy mapping | Map risk signals to platform policy categories |
| Fallback routing | Use stronger models for sensitive cases |
A practical workflow:
image → deepfake detector → metadata/quality checks → risk report → LLMAPI summary/routing → human review
The detector finds signals. LLMAPI helps explain and route them.
Common mistakes
These are the classics.
| Mistake | Better approach |
| Treating detector score as proof | Use it as a risk signal |
| Testing only obvious AI images | Test real-world hard cases |
| Ignoring compression/screenshots | Include them in evaluation |
| Ignoring false positives | Track real images wrongly flagged |
| No face cropping | Crop faces for facial deepfake tasks |
| No metadata checks | Add EXIF/provenance context |
| No model version logging | Store model name/version with every result |
| No review queue | Send uncertain/high-risk cases to humans |
| Using one threshold for everything | Tune thresholds by workflow risk |
| No re-testing | Re-evaluate as generators change |
The biggest mistake is building a detector that sounds more certain than it is.
Deepfake detection should help review. It should not become a judge.
A safer production workflow
Here is the workflow we would actually ship:
- User uploads image.
- App validates file type and size.
- App stores image securely.
- Python extracts metadata.
- Python checks image quality.
- Python detects and crops faces.
- Classifier analyzes full image and face crops.
- App combines signals into risk score.
- Low-risk images pass.
- Unclear images enter review queue.
- High-risk images go to specialist review.
- App logs model version, scores, warnings, and final decision.
- Human feedback improves future thresholds/evaluation.
That is much more defensible than one fake/real label.
The practical takeaway
You can detect deepfake images with Python, but you should build the system carefully.
Use metadata checks for context. Use image quality checks so you know when detection is unreliable. Use face crops for facial deepfake detection. Use a pretrained classifier as one signal, not the only signal. Batch results into a review queue. Track false positives and false negatives. Keep model versions. Re-test often. Use LLMAPI to summarize and route detection reports when the workflow needs human-readable review notes or actions.
The best deepfake detection workflow looks like this:
image → metadata → quality checks → face crops → classifier → risk score → review when needed
That is the safe way to use deepfake detection in real products.
Not “this is definitely fake.”
More like:
Here are the signals.
Here is the uncertainty.
Here is what should be checked next.