Analyze Images with Azure AI Vision
Azure AI Vision (Image Analysis 4.0) is Microsoft's core computer vision service — it analyses images and returns structured information about what the image contains: objects, text, people, colours, faces, and generated captions. It also provides the OCR Read API for extracting text from any image or document, and the Face API for face detection and identification.
Azure AI Vision terms — defined before code
You know REST APIs, endpoints, API keys, and async POST-then-poll from Parts 1 and 5. The OCR Read API uses that exact same async pattern. You know confidence scores from Document Intelligence. Every term below is brand new for this topic.
Microsoft's core computer vision service for analysing images. Version 4.0 (the current exam version) uses the Florence foundation model under the hood — a large vision model trained on billions of images. Accessed via the azure-ai-vision-imageanalysis Python SDK or REST API. Provides visual features on demand without training your own model.
A specific type of information you request from Image Analysis. You specify which features you want analysed — you are not charged for features you don't request. Features include: CAPTION, DENSE_CAPTIONS, TAGS, OBJECTS, PEOPLE, SMART_CROPS, READ (OCR). Each feature adds specific data to the JSON response.
A single AI-generated sentence describing the entire image in natural language. Example: "A person riding a bicycle on a city street". Returned as result.caption.text with a confidence score. Enabled by requesting the CAPTION visual feature. New in Image Analysis 4.0 — not available in older API versions.
Multiple captions describing different areas of the image — one overall caption plus individual captions for each detected object region. Each dense caption includes its own bounding box coordinates. Useful for accessibility (screen readers) and detailed image cataloguing. Enabled by the DENSE_CAPTIONS feature.
A list of descriptive keywords identified in the image — covering objects, living beings, scenery, actions, and colours. Example: image of a sunset → tags: [beach, sunset, ocean, orange sky, horizon]. Each tag has a confidence score. Tags are broader and more numerous than object detections. Enabled by the TAGS feature.
Identifies specific physical objects in an image and returns a bounding box for each one — the rectangular coordinates of exactly where in the image the object appears. Objects are more specific than tags and include location. Example: "car at [x:120, y:80, width:340, height:200]". Enabled by the OBJECTS feature.
The rectangular coordinates (x, y, width, height) of where an object or text appears in an image. x and y are the top-left corner position in pixels. Width and height are the dimensions of the rectangle. Used by object detection and OCR to tell you precisely where in the image each element was found. Essential for drawing highlight boxes on images.
Detects people in an image and returns a bounding box for each person detected. Does NOT identify who the person is — only that a person is present and where. This is distinct from Face API identification. People detection is not a Limited Access feature — anyone can detect the presence of people. Enabled by the PEOPLE feature.
Suggests optimal cropping regions of an image at various aspect ratios — focusing on the most visually important content. Useful for automatically generating image thumbnails for different screen sizes (16:9 for widescreen, 1:1 for social media). You specify desired aspect ratios; the service returns recommended crop rectangles. Enabled by the SMART_CROPS feature.
Separates the foreground subject from the background in an image, returning either the foreground-only image (transparent background PNG) or a binary matte image (white = foreground, black = background). Useful for product photography, profile pictures. Part of the Image Analysis API — enabled via the segmentationMode parameter.
Extracts printed and handwritten text from images and multi-page PDFs. Uses an asynchronous pattern (same as Document Intelligence): POST image → receive operation URL → poll GET until "succeeded" → read text. Returns text organised as pages → lines → words, each with bounding polygon coordinates. Part of Azure AI Vision (same endpoint, different operation path).
The structured output of the OCR Read API. Organised hierarchically: result.pages → page.lines → line.words. Each word has: .content (the word text), .confidence (0–1 recognition accuracy), .polygon (bounding coordinates as [x1,y1, x2,y2, x3,y3, x4,y4] — a quadrilateral, not necessarily a rectangle, to handle rotated text).
A separate Azure AI service (distinct from Image Analysis) focused on face processing. Capabilities: detection (is there a face? where?), attribute analysis (head pose, blur, exposure, accessories — emotions were deprecated), identification (who is this person? — requires a PersonGroup with enrolled faces). Face identification is a Limited Access feature requiring Microsoft approval.
A container for enrolled, known faces used for face identification. Workflow: Create PersonGroup → Add Persons (each with a name) → Add face images for each Person → Train the PersonGroup → Call Identify() with a detected face → returns the closest matching Person(s) with confidence. The PersonGroup must be re-trained whenever faces are added or removed.
Certain high-sensitivity Azure AI Vision capabilities require Microsoft's approval before use in production: Face identification (matching faces to known people), Celebrity recognition, and features that could identify individuals. Free tier/basic usage of face detection (is a face present?) is not gated. Only features that ID specific people require approval.
What you can ask the Vision API to detect
Imagine an image arrives at a team of consultants. You don't pay all of them — you only call in the ones you need.
You call the tagger to list what's in the image. You call the object locator to draw boxes
around specific things. You call the caption writer to describe it in English. You call the
text reader to extract any printed text. Each specialist returns their results, charges only for
their work, and gives you a confidence score for how certain they were.
Azure AI Vision works exactly this way — you specify which VisualFeatures to include in your
request, and only those analyses are performed and returned.
All visual features — what each one returns
One natural-language sentence describing the whole image. result.caption.text, .confidence. Example: "A dog sitting on a green lawn".
Up to 10 captions for different regions of the image. Each has text, confidence, and bounding box. First item is always the whole-image caption.
List of keyword tags covering objects, scenery, actions, colours. Each tag has name + confidence. More numerous and broader than OBJECTS.
Detected physical objects with bounding boxes (x, y, width, height in pixels) and confidence. More precise location than tags — tells you WHERE each object is.
Detects human figures — bounding box for each person. Does NOT identify WHO they are. Not Limited Access. Returns confidence per person detection.
Extracts printed and handwritten text from the image (OCR). Returns pages → lines → words, each with bounding polygon. Handles rotated and handwritten text.
Recommends crop rectangles at specified aspect ratios that keep the most important content. Used for generating optimised thumbnails.
*Separate parameter (not a VisualFeature). Returns foreground-only PNG or binary matte. Called with segmentationMode="backgroundRemoval" or "foregroundMatting".
Tags: keywords only, no location — "dog", "park", "sunny" — for labelling and search
Objects: specific things WITH bounding boxes — for drawing boxes on images, counting items
People: humans with bounding boxes — for counting people, presence detection. NOT identification
Caption: one sentence description of the whole image — for accessibility alt text, image cataloguing
Dense Captions: multiple descriptions for different image regions — for detailed accessibility or content cataloguing
Extracting text from images and documents
The Read API is Azure's most powerful OCR engine — it handles printed text, handwriting, and mixed content. It is accessed as part of Azure AI Vision (same resource, same endpoint) using the READ visual feature in Image Analysis 4.0.
Imagine photographing a handwritten recipe card. A very precise typist reads every word, records the exact
text, notes which line each word was on, and measures exactly where on the card each word appeared
(in case you need to highlight it). That typist is the Read API.
The hierarchy is: Page (the full image) → Lines (each row of text) →
Words (each individual word with its exact position and confidence score).
What makes the Read API different from simple OCR
| Capability | Old OCR API (v3.2) | Read API / Image Analysis 4.0 |
|---|---|---|
| Handwritten text | Limited support | ✅ Full handwriting recognition |
| Multi-page PDFs | ❌ Images only | ✅ PDFs up to 2,000 pages |
| Rotated / skewed text | Limited | ✅ Polygon bounding boxes handle any rotation |
| Output structure | Lines → words | Pages → lines → words (with polygon per word) |
| Speed | Faster (synchronous) | For Image Analysis 4.0: direct (not separate async polling for images) |
| Handwriting styles | Basic | ✅ Multiple languages, cursive, print mix |
Face detection, attributes, and identification
The exam heavily tests this distinction. Face detection (is there a face? where is it? is it blurry?) is available to everyone without approval. Face identification (who is this person? match to a PersonGroup) requires Microsoft's Limited Access approval. Many exam questions describe a face identification scenario and ask what's required — the answer always includes obtaining Limited Access approval.
Face detection attributes — what you get without approval
| Attribute | What it returns | Limited Access? |
|---|---|---|
| FaceRectangle | Bounding box of the face: top, left, width, height | ❌ No |
| HeadPose | Pitch, roll, yaw — the angle the head is tilted/turned | ❌ No |
| Blur | Is the face in or out of focus — Low/Medium/High | ❌ No |
| Exposure | Is the face correctly lit — UnderExposure/GoodExposure/OverExposure | ❌ No |
| Noise | Graininess of the face image | ❌ No |
| Accessories | Glasses, headwear, mask — present/absent | ❌ No |
| Facial Hair | Beard, moustache, sideburns — confidence scores | ❌ No |
| Glasses | NoGlasses/ReadingGlasses/Sunglasses/SwimmingGoggles | ❌ No |
| Emotion ⚠️ | DEPRECATED — removed from the API | — |
| Identification | Matching face to a known person in a PersonGroup | ✅ YES — Limited Access required |
| Verification | Are two face photos the same person? | ✅ YES — Limited Access required |
Face Identification Workflow (requires Limited Access)
A PersonGroup is a named container for all the known people you want to identify. Create it with a unique ID and name. Example: person_group_id = "company-employees". One PersonGroup per use case.
For each person you want to identify, create a Person object inside the PersonGroup. The Person object has a name (e.g. "Alice Johnson") and gets a unique person_id. The Person object is the container for that person's face photos.
Upload multiple face photos for each Person (different angles, lighting, expressions recommended — 10+ photos per person for good accuracy). Each photo must clearly show the person's face. The Face API extracts the face features from each photo and associates them with the Person.
Call train_person_group() on the PersonGroup. This builds the identification model from all the enrolled face photos. Training is async — poll for completion before calling Identify(). Must re-train every time faces are added or removed.
First detect faces in the new image to get face_ids. Then call identify() passing the face_ids and the PersonGroup ID. Returns a list of candidates for each detected face — each candidate has a person_id and confidence score. Filter candidates above your confidence threshold.
Analysing images, extracting text, and reading OCR results
Install: pip install azure-ai-vision-imageanalysis azure-core
import os
from azure.ai.vision.imageanalysis import ImageAnalysisClient
from azure.ai.vision.imageanalysis.models import VisualFeatures
# VisualFeatures is an enum — each value requests one type of image analysis
from azure.core.credentials import AzureKeyCredential
# ── Connection details ─────────────────────────────────────────────────
# From your Azure AI Vision resource in the portal: Keys and Endpoint
# Endpoint format: https://<your-resource-name>.cognitiveservices.azure.com/
endpoint = os.environ.get("VISION_ENDPOINT")
key = os.environ.get("VISION_KEY")
# Create the client — wraps the endpoint and credential for all Vision operations
client = ImageAnalysisClient(
endpoint = endpoint,
credential = AzureKeyCredential(key)
)
# ══════════════════════════════════════════════════════════════════════
# EXAMPLE 1 — Full image analysis from a URL
# Requesting multiple visual features in a single API call
# ══════════════════════════════════════════════════════════════════════
image_url = "https://upload.wikimedia.org/wikipedia/commons/thumb/4/47/PNG_transparency_demonstration_1.png/280px-PNG_transparency_demonstration_1.png"
# analyze() is SYNCHRONOUS for URL-based images — the result comes back immediately
# (No polling needed for Image Analysis 4.0 with URLs — only the standalone Read API is async)
# visual_features: a list of VisualFeatures enum values — only these are analysed and billed
result = client.analyze(
image_url = image_url,
visual_features = [
VisualFeatures.CAPTION, # Generate a one-sentence image description
VisualFeatures.TAGS, # List of descriptive keyword tags
VisualFeatures.OBJECTS, # Detected objects with bounding boxes
VisualFeatures.PEOPLE, # Detected human figures with bounding boxes
VisualFeatures.READ, # OCR — extract any text in the image
VisualFeatures.SMART_CROPS, # Suggest crop regions for thumbnails
],
# smart_crops_aspect_ratios: list of desired width/height ratios for crop suggestions
# 1.0 = square crop (e.g. social media profile), 1.78 = landscape widescreen 16:9
smart_crops_aspect_ratios = [1.0, 1.78],
# gender_neutral_caption: True = avoid gendered pronouns in captions (he/she → they)
gender_neutral_caption = True
)
# ── Caption ──────────────────────────────────────────────────────────
# result.caption is None if CAPTION was not in visual_features
if result.caption:
# .text: the generated sentence — .confidence: how confident the model is (0.0 to 1.0)
print(f"Caption: {result.caption.text} (confidence: {result.caption.confidence:.2f})")
# ── Tags ──────────────────────────────────────────────────────────────
if result.tags:
print("\nTags:")
# result.tags.list: a list of ImageTag objects, sorted by confidence descending
for tag in result.tags.list:
# tag.name: the keyword (e.g. "car", "outdoor") — tag.confidence: certainty score
print(f" {tag.name}: {tag.confidence:.2f}")
# ── Objects with bounding boxes ────────────────────────────────────────
if result.objects:
print("\nObjects:")
# result.objects.list: each item is a DetectedObject
for obj in result.objects.list:
# obj.tags: what type of object — obj.bounding_box: where in the image it is
name = obj.tags[0].name if obj.tags else "unknown"
bb = obj.bounding_box
# bb.x, bb.y: top-left corner pixel position
# bb.width, bb.height: dimensions of the rectangle in pixels
print(f" {name}: x={bb.x}, y={bb.y}, w={bb.width}, h={bb.height}")
# ── People detection ──────────────────────────────────────────────────
if result.people:
print(f"\nPeople detected: {len(result.people.list)}")
for person in result.people.list:
bb = person.bounding_box
# person.confidence: how confident the model is this is a human figure
print(f" Person at x={bb.x}, y={bb.y} — confidence: {person.confidence:.2f}")
# ── OCR text extraction (READ feature) ────────────────────────────────
if result.read:
print("\nExtracted Text:")
# result.read.blocks: list of text blocks (logical groups of text)
# Each block has .lines; each line has .words
for block in result.read.blocks:
for line in block.lines:
# line.text: the full text of this line (all words concatenated)
print(f" Line: '{line.text}'")
for word in line.words:
# word.text: the individual word — word.confidence: OCR confidence
# word.bounding_polygon: list of {x, y} points forming the word's quadrilateral
# (4 points, not necessarily rectangular — handles rotated/slanted text)
pts = [(p.x, p.y) for p in word.bounding_polygon]
print(f" Word: '{word.text}' conf={word.confidence:.2f} at {pts}")
# ── Smart crop suggestions ─────────────────────────────────────────────
if result.smart_crops:
print("\nSmart Crop Suggestions:")
for crop in result.smart_crops.list:
# crop.aspect_ratio: the ratio you requested (e.g. 1.0 for square)
# crop.bounding_box: the suggested crop rectangle
bb = crop.bounding_box
print(f" Ratio {crop.aspect_ratio}: x={bb.x}, y={bb.y}, w={bb.width}, h={bb.height}")
# ══════════════════════════════════════════════════════════════════════
# EXAMPLE 2 — Analysing a LOCAL IMAGE FILE
# Use analyze_from_url() for URLs, analyze() with image_data for local files
# ══════════════════════════════════════════════════════════════════════
# Read the local image file as binary bytes
with open("local-photo.jpg", "rb") as f:
image_bytes = f.read() # Read the entire file as raw binary
# analyze() also accepts image_data (bytes) instead of image_url
# image_data: pass the binary content and specify the MIME type
result_local = client.analyze(
image_data = image_bytes, # The image as raw bytes
visual_features = [VisualFeatures.CAPTION, VisualFeatures.TAGS]
)
if result_local.caption:
print(f"\nLocal image caption: {result_local.caption.text}")
Python — Face API: Detection and Identification
Install: pip install azure-ai-vision-face · Note: requires Limited Access approval for identification and verification.
from azure.ai.vision.face import FaceClient
from azure.ai.vision.face.models import (
FaceDetectionModel, # Which version of the face detection model to use
FaceRecognitionModel, # Which version of the face recognition model for identification
FaceAttributeTypeDetection01, # Attributes available in the detection model
)
from azure.core.credentials import AzureKeyCredential
# Face API uses its OWN endpoint — different from Image Analysis
# Face API endpoint format: https://<region>.api.cognitive.microsoft.com/
FACE_ENDPOINT = os.environ.get("FACE_ENDPOINT")
FACE_KEY = os.environ.get("FACE_KEY")
face_client = FaceClient(endpoint=FACE_ENDPOINT, credential=AzureKeyCredential(FACE_KEY))
# ── STEP 1: Detect faces in an image ──────────────────────────────────
# This does NOT identify anyone — just finds where faces are and which attributes they have
# No Limited Access approval needed for this step
detected_faces = face_client.detect_from_url(
url = "https://example.com/team-photo.jpg",
# detection_model: DETECTION_03 is latest — best for small faces and masks
detection_model = FaceDetectionModel.DETECTION_03,
# recognition_model: needed if you plan to identify or verify — must match training
recognition_model = FaceRecognitionModel.RECOGNITION_04,
# return_face_id: needed for identification — face IDs expire after 24 hours
return_face_id = True,
# return_face_attributes: which detection attributes to analyse
return_face_attributes = [
FaceAttributeTypeDetection01.HEAD_POSE, # Pitch, roll, yaw angles
FaceAttributeTypeDetection01.BLUR, # Focus quality
FaceAttributeTypeDetection01.EXPOSURE, # Lighting quality
FaceAttributeTypeDetection01.GLASSES, # Eyewear type
]
)
print(f"Found {len(detected_faces)} face(s)")
for face in detected_faces:
# face.face_id: a temporary ID used for identification (expires after 24 hours)
print(f"\nFace ID: {face.face_id}")
# face_rectangle: where on the image the face appears
r = face.face_rectangle
print(f" Location: top={r.top}, left={r.left}, width={r.width}, height={r.height}")
# face_attributes: the attributes we requested above
attrs = face.face_attributes
if attrs:
# head_pose: how the head is oriented
if attrs.head_pose:
print(f" Head Pose: pitch={attrs.head_pose.pitch:.1f} roll={attrs.head_pose.roll:.1f} yaw={attrs.head_pose.yaw:.1f}")
# blur: is the face in focus? .blur_level = LOW / MEDIUM / HIGH
if attrs.blur:
print(f" Blur: {attrs.blur.blur_level}")
# glasses: what eyewear is the person wearing (if any)
if attrs.glasses:
print(f" Glasses: {attrs.glasses}")
# ── STEP 2: IDENTIFICATION (requires Limited Access approval) ──────────
# The code below demonstrates PersonGroup creation and identification
# This will NOT work without Microsoft Limited Access approval
GROUP_ID = "company-employees"
# Create the PersonGroup (only done once — reuse for all identifications)
face_client.person_group.create(
person_group_id = GROUP_ID, # Unique ID for this group (lowercase, no spaces)
name = "Company Employees", # Human-readable name
recognition_model = FaceRecognitionModel.RECOGNITION_04 # Must match detection model
)
# Add a Person (Alice) to the group — returns a person_id
alice = face_client.person_group_person.create(
person_group_id = GROUP_ID,
name = "Alice Johnson" # The person's name — stored for reference
)
# alice.person_id is a UUID that uniquely identifies Alice in this group
# Add face images for Alice (multiple photos from different angles recommended)
for photo_url in ["https://example.com/alice1.jpg", "https://example.com/alice2.jpg"]:
face_client.person_group_person.add_face_from_url(
person_group_id = GROUP_ID,
person_id = alice.person_id, # Link this photo to Alice
url = photo_url
)
# Train the PersonGroup — must be done before identification
# This is also async — call get_training_status() until status is "succeeded"
face_client.person_group.train(person_group_id=GROUP_ID)
print("PersonGroup training initiated — poll get_training_status() until succeeded")
# Identify a detected face against the PersonGroup
# detected_faces[0].face_id is the temporary face_id from the DETECT step above
identify_results = face_client.identify(
face_ids = [detected_faces[0].face_id], # Which faces to identify
person_group_id = GROUP_ID, # Which group to search against
)
for id_result in identify_results:
if id_result.candidates:
# candidates: sorted by confidence descending — first candidate is the best match
best = id_result.candidates[0]
# best.person_id: the UUID of the matched person — look up Alice by this ID
# best.confidence: 0.0 to 1.0 — typically use 0.5+ as threshold for a match
print(f"Identified: {best.person_id} with confidence {best.confidence:.2f}")
else:
print("No match found in PersonGroup")
Azure AI Vision — what the exam tests
1. Face detection ≠ Face identification — Detection (where is the face? is it blurry?) is free and available to all. Identification (who is this person?) requires Limited Access approval. The most common exam trap in this topic.
2. Emotion attributes were deprecated — The exam may try to trick you into selecting "Emotion" as a Face API attribute. Emotion detection was removed from the API. Don't select it.
3. PersonGroup must be trained before identification — You cannot call Identify() on an untrained PersonGroup. Adding new faces requires re-training. Training is asynchronous — poll for completion.
4. Tags vs Objects vs People — distinct features — Tags are keywords (no location). Objects are specific things with bounding boxes. People are human figures with bounding boxes but no identity. Know exactly when to use each.
5. CAPTION is new in Image Analysis 4.0 — Older API versions (v3.2) don't have the Caption feature. If the exam mentions needing a natural-language image description, the answer uses Image Analysis 4.0 with the CAPTION feature.
6. OCR polygon vs bounding box — Object detection uses bounding_box (rectangles: x, y, width, height). OCR word positions use bounding_polygon (4 points: handles rotated/skewed text). Different structures — don't confuse them in code questions.
7. Face identification needs a PersonGroup — No PersonGroup = no identification. The workflow is always: Create Group → Add Persons → Add Faces → Train → Detect → Identify. All 5 steps in order.
Test your understanding
Which Azure AI Vision feature should be used, and with which API version?
B is correct. The CAPTION visual feature in Image Analysis 4.0 generates a single natural-language sentence describing the full image — exactly what's needed for alt-text. This feature was introduced in v4.0 and is not available in v3.2. v3.2 had a "Description" feature, but Content captions are the v4.0 equivalent and use the Florence foundation model for higher quality output. TAGS returns keywords, not sentences. Face API analyses faces, not image content generally.