Skip to main content
Lesson 10 of 12 · Vision and Image Analysis via API

Compare Images with the Vision API

Lesson 10 of 12 Updated November 2, 2026 Developer
On this page
  1. Visual Comparative Analysis
  2. Before/After Comparison
  3. Product Quality Control
  4. Tracking Evolution Over Time
  5. Comparing User Interfaces
  6. Comparison Best Practices
  7. Key Takeaways

Visual Comparative Analysis

Image comparison is a powerful use case that leverages the multi-image capability covered in Lesson 6. The question shifts from “What does this image show?” to “What has changed?”—an operation that a semantic model handles fundamentally differently than a pixel comparator. Where a pixel difference signals a divergence without qualifying it, the model can state that a label shifted two millimeters and a logo disappeared, then judge which of the two matters.

Quality control, evolution tracking, visual regression detection: this lesson shows how to structure comparison prompts and leverage the results.

Before/After Comparison

The most common scenario involves verifying what changed between two states. The system message defines the comparison method, while the user message identifies the images:

from mistralai.client import Mistral
import os

client = Mistral(api_key=os.environ["MISTRAL_API_KEY"])

response = client.chat.complete(
    model="mistral-large-latest",
    messages=[
        {
            "role": "system",
            "content": """You are an expert in comparative image analysis. When presented with two images:
1. Briefly describe each image
2. List identified differences (with location)
3. Assess the significance of each difference (minor/major)
4. Provide an overall similarity score (0-100%)"""
        },
        {
            "role": "user",
            "content": [
                {"type": "text", "text": "BEFORE Image (reference):"},
                {"type": "image_url", "image_url": "https://exemple.com/avant.jpg"},
                {"type": "text", "text": "AFTER Image (current state):"},
                {"type": "image_url", "image_url": "https://exemple.com/apres.jpg"},
                {"type": "text", "text": "Identify all differences between these two images."}
            ]
        }
    ]
)

print(response.choices[0].message.content)

Interleaving text (“BEFORE Image”, “AFTER Image”) helps the model identify the role of each image. The third step in the system prompt is what makes the report actionable: without prioritization, you receive a list where a shadow change weighs as much as a missing part.

Product Quality Control

At the end of the pipeline, this report must be read by a program, not a human. We therefore combine comparison with the JSON mode from Lesson 9: the model returns a verdict, a score, and a list of qualified defects that code can immediately exploit.

import base64
import json

def encode_image(path):
    with open(path, "rb") as f:
        b64 = base64.standard_b64encode(f.read()).decode("utf-8")
    return f"data:image/jpeg;base64,{b64}"

def quality_control(reference_image, sample_image):
    """Compares a sample to its reference and detects defects."""
    response = client.chat.complete(
        model="mistral-large-latest",
        response_format={"type": "json_object"},
        messages=[
            {
                "role": "system",
                "content": "You are a quality inspector. Compare the sample to the reference and return a JSON report."
            },
            {
                "role": "user",
                "content": [
                    {"type": "text", "text": "Reference (compliant product):"},
                    {"type": "image_url", "image_url": encode_image(reference_image)},
                    {"type": "text", "text": "Sample to inspect:"},
                    {"type": "image_url", "image_url": encode_image(sample_image)},
                    {
                        "type": "text",
                        "text": """Compare and return:
{
  "compliant": true/false,
  "compliance_score": 0-100,
  "defects": [
    {"description": "...", "severity": "minor/major/critical", "location": "..."}
  ],
  "recommendation": "accept/review/reject"
}"""
                    }
                ]
            }
        ]
    )

    return json.loads(response.choices[0].message.content)

# Usage
result = quality_control("reference/product-ok.jpg", "batch-42/sample-7.jpg")

if result["compliant"]:
    print(f"COMPLIANT - Score: {result['compliance_score']}%")
else:
    print(f"NON-COMPLIANT - {len(result['defects'])} defect(s) detected")
    for defect in result["defects"]:
        print(f"  - [{defect['severity']}] {defect['description']}")

The recommendation field is more interesting than it appears: by distinguishing “review” from “reject,” it reserves human intervention for ambiguous cases instead of imposing it everywhere or nowhere.

Tracking Evolution Over Time

A time series—construction site, crop growth, structure degradation—is treated as an extended comparison, provided you label each image with its date. The model then has the temporal axis and can qualify a pace, not just a gap.

def analyze_evolution(chronological_images, context=""):
    """Analyzes visible evolution across a series of ordered images."""
    content = [
        {
            "type": "text",
            "text": f"Here are {len(chronological_images)} images taken at different times, in chronological order. {context}"
        }
    ]

    for i, (date, path) in enumerate(chronological_images):
        content.append({"type": "text", "text": f"Image {i+1} ({date}):"})
        content.append({"type": "image_url", "image_url": encode_image(path)})

    content.append({
        "type": "text",
        "text": "Describe the evolution observed between each step. Identify major changes and the general trend."
    })

    response = client.chat.complete(
        model="mistral-large-latest",
        messages=[{"role": "user", "content": content}]
    )

    return response.choices[0].message.content

# Construction site tracking
images = [
    ("2025-01", "site/jan-2025.jpg"),
    ("2025-06", "site/jun-2025.jpg"),
    ("2026-01", "site/jan-2026.jpg"),
]

evolution = analyze_evolution(images, context="Tracking a construction site.")
print(evolution)

The final question is added only after all images, once the complete file is laid out.

Comparing User Interfaces

Web development offers an immediate use case: confronting the approved mockup with the actually produced render. Where a pixel regression test fails on a one-pixel shift of no importance, the model qualifies the impact of each discrepancy.

def compare_interfaces(expected_screenshot, actual_screenshot):
    """Compares two interface screenshots to detect visual regressions."""
    response = client.chat.complete(
        model="mistral-large-latest",
        response_format={"type": "json_object"},
        messages=[
            {
                "role": "system",
                "content": "You are a QA tester specializing in visual testing of web interfaces."
            },
            {
                "role": "user",
                "content": [
                    {"type": "text", "text": "Expected design (mockup):"},
                    {"type": "image_url", "image_url": encode_image(expected_screenshot)},
                    {"type": "text", "text": "Current render (screenshot):"},
                    {"type": "image_url", "image_url": encode_image(actual_screenshot)},
                    {
                        "type": "text",
                        "text": """Compare and return:
{
  "pixel_perfect": true/false,
  "differences": [
    {"element": "...", "expected": "...", "actual": "...", "impact": "minor/moderate/major"}
  ],
  "fidelity_score": 0-100,
  "verdict": "compliant/minor_adjustments/regression"
}"""
                    }
                ]
            }
        ]
    )

    return json.loads(response.choices[0].message.content)

Plugged into your continuous integration pipeline, this check blocks delivery only on a regression verdict.

Comparison Best Practices

These examples share a few principles worth making explicit. Always name your images with an interleaved label—“Reference,” “Sample,” “Before,” “After”—because without it, the model assigns roles based on order randomness. Ensure shot comparability: at different angles and lighting, the model will signal discrepancies that stem only from photo conditions. Use Mistral Large 3, because comparison mobilizes fine-grained reasoning that smaller models do not always master. Limit yourself to two or three images per call; beyond that, comparative precision dilutes. And systematically request a structured format, with a score, listed differences, and a verdict, if the result must be processed automatically.

Key Takeaways

  • Image comparison leverages the API’s multi-image capability
  • Interleave explanatory text between images to clarify their roles
  • JSON mode (response_format) is ideal for automated comparison reports
  • Primary use cases: quality control, site tracking, visual QA, before/after
  • Always use Mistral Large 3 for demanding comparison tasks