LLM Guides

How to Detect Deepfake Images Using Python

Jul 16, 2026

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:

TypeExample
Fully AI-generated imageA fake person generated from scratch
Face swapOne person’s face placed onto another body
Face morphA face blended from two identities
Attribute editAge, hair, expression, or identity changed
InpaintingPart of the image added or replaced
AI upscaling/restorationReal image modified by AI tools
Screenshot of AI imageGenerated image reposted as a screenshot
Compressed AI imageFake 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:

  1. A risk score.
  2. Supporting signals.
  3. Review recommendations.
  4. 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:

  1. Metadata inspection
    Check EXIF data, software tags, missing camera info, and suspicious editing traces.
  2. Image quality checks
    Look at blur, resolution, compression, and whether the image is too degraded for reliable analysis.
  3. Face-aware preprocessing
    Detect and crop faces so the classifier can focus on the manipulated region.
  4. Pretrained classifier inference
    Use a Hugging Face image classification model or your own trained model.
  5. 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:

PackageWhy
PillowOpen images and inspect EXIF metadata
OpenCVImage preprocessing, blur checks, face detection
NumPyImage arrays and scoring
PandasBatch reports
TransformersRun pretrained image classifiers
TorchModel inference
MediaPipeFace detection/cropping option
scikit-imageOptional 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:

  1. Camera make.
  2. Camera model.
  3. Date taken.
  4. Lens info.
  5. GPS data.
  6. Exposure settings.
  7. 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:

  1. Metadata score.
  2. Full-image model score.
  3. Face-crop model score.
  4. 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:

  1. File type validation.
  2. Max file size.
  3. Malware scanning.
  4. Authentication.
  5. Rate limits.
  6. Logging.
  7. Human review queue.
  8. Model version tracking.
  9. Secure storage rules.
  10. 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:

SignalWhat it adds
Deepfake classifierVisual/manipulation risk
EXIF metadataCamera/editing context
Provenance credentialsStronger authenticity signal when present
Reverse image searchWhere else image appeared
Face/source comparisonIdentity consistency
Human reviewFinal 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:

  1. Use a model trained for your exact problem.
  2. Crop faces if detecting facial deepfakes.
  3. Train or fine-tune on real examples from your workflow.
  4. Include compressed, cropped, upscaled, and screenshotted images.
  5. Include real edited images as negative examples.
  6. Track false positives and false negatives.
  7. Use separate thresholds for different risk levels.
  8. Keep model versions and evaluation sets.
  9. Re-test when new generators appear.
  10. 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:

  1. Real camera images.
  2. AI-generated portraits.
  3. Face swaps.
  4. Face morphs.
  5. Edited real photos.
  6. Screenshots of generated images.
  7. JPEG-compressed images.
  8. Cropped images.
  9. Upscaled images.
  10. Social media downloads.
  11. Low-light images.
  12. Multiple ethnicities, ages, genders, and lighting conditions.
  13. Negative examples that look suspicious but are real.

Track:

MetricWhy it matters
AccuracyOverall correct predictions
PrecisionHow many flagged images are really fake
RecallHow many fake images are caught
False positive rateReal images wrongly flagged
False negative rateFake images missed
AUCRanking quality across thresholds
CalibrationWhether scores match real risk
RobustnessPerformance after compression/cropping
Bias/fairnessWhether groups are flagged differently
Review workloadHow 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 fieldExample
Risk labelNeeds review
Risk score78%
SignalsModel score high, EXIF missing, face crop suspicious
Quality warningImage is low resolution
Model versiondetector_v3.2
RecommendationSend to manual review
WarningThis 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:

TaskExample
Review summary“This image needs review because model and metadata signals are suspicious.”
Moderation noteDraft an internal reviewer explanation
Case routingSend high-risk identity images to fraud review
User-facing wordingWrite a careful non-accusatory message
Batch reportSummarize weekly fake-image trends
Evidence checklistList what a reviewer should verify next
Policy mappingMap risk signals to platform policy categories
Fallback routingUse 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.

MistakeBetter approach
Treating detector score as proofUse it as a risk signal
Testing only obvious AI imagesTest real-world hard cases
Ignoring compression/screenshotsInclude them in evaluation
Ignoring false positivesTrack real images wrongly flagged
No face croppingCrop faces for facial deepfake tasks
No metadata checksAdd EXIF/provenance context
No model version loggingStore model name/version with every result
No review queueSend uncertain/high-risk cases to humans
Using one threshold for everythingTune thresholds by workflow risk
No re-testingRe-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:

  1. User uploads image.
  2. App validates file type and size.
  3. App stores image securely.
  4. Python extracts metadata.
  5. Python checks image quality.
  6. Python detects and crops faces.
  7. Classifier analyzes full image and face crops.
  8. App combines signals into risk score.
  9. Low-risk images pass.
  10. Unclear images enter review queue.
  11. High-risk images go to specialist review.
  12. App logs model version, scores, warnings, and final decision.
  13. 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.

Deploy in minutes