Florence-2: Microsoft's Multi-Task Vision Foundation Model
Table of Contents
Introduction
Florence-2 is a vision foundation model from Microsoft designed to handle a wide variety of computer vision tasks with a single unified model. Unlike traditional models trained for one specific task, Florence-2 uses a sequence-to-sequence approach that enables captioning, object detection, OCR, segmentation, visual grounding, and more simply by changing the text prompt.
Key advantages of Florence-2:
- Multi-task in one model: A single model for captioning, detection, OCR, segmentation, and more
- Prompt-based: Simply change the text prompt to switch between tasks
- Lightweight and efficient: Available in base (232M parameters) and large (771M parameters) variants
- Zero-shot capability: Can be used directly without fine-tuning for many tasks
- Fine-tunable: Can be fine-tuned on custom datasets for improved performance
In this tutorial, you will learn to use Florence-2 for various vision tasks, perform batch processing, fine-tune on custom datasets, and build a practical document analysis pipeline.
Prerequisites
- Python 3.9 or higher
- GPU with at least 8GB VRAM (for base model) or 16GB VRAM (for large model)
- Basic understanding of PyTorch and Hugging Face Transformers
- Familiarity with basic computer vision concepts (bounding boxes, segmentation, etc.)
Installation
# Install main dependencies
pip install torch torchvision torchaudio
Install transformers and Florence-2 dependencies
pip install transformers einops timm flashattn
Install additional libraries for image processing
pip install Pillow opencv-python matplotlib
Install supervision for visualization
pip install supervision
Install libraries for fine-tuning
pip install datasets peft accelerate
Verify the installation:
import torch
import transformers
print(f"PyTorch version: {torch.version}")
print(f"Transformers version: {transformers.version}")
print(f"CUDA available: {torch.cuda.isavailable()}")
if torch.cuda.isavailable():
print(f"GPU: {torch.cuda.getdevicename(0)}")
print(f"VRAM: {torch.cuda.getdeviceproperties(0).totalmem / 1e9:.1f} GB")
Loading Florence-2 Models
Florence-2 comes in two size variants. Choose based on your needs and hardware capacity.
Florence-2-base
from transformers import AutoProcessor, AutoModelForCausalLM
import torch
Load base model (232M parameters)
modelid = "microsoft/Florence-2-base"
processor = AutoProcessor.frompretrained(modelid, trustremotecode=True)
model = AutoModelForCausalLM.frompretrained(
modelid,
torchdtype=torch.float16,
trustremotecode=True
).to("cuda")
print("Florence-2-base loaded successfully!")
Florence-2-large
# Load large model (771M parameters) for higher accuracy
modelid = "microsoft/Florence-2-large"
processor = AutoProcessor.frompretrained(modelid, trustremotecode=True)
model = AutoModelForCausalLM.frompretrained(
modelid,
torchdtype=torch.float16,
trustremotecode=True
).to("cuda")
print("Florence-2-large loaded successfully!")
Utility Function for Inference
from PIL import Image
import requests
from io import BytesIO
def loadimage(imagepathorurl):
"""Load an image from a local path or URL."""
if imagepathorurl.startswith("http"):
response = requests.get(imagepathorurl)
image = Image.open(BytesIO(response.content)).convert("RGB")
else:
image = Image.open(imagepathorurl).convert("RGB")
return image
def runflorence(image, taskprompt, textinput=None):
"""Run Florence-2 inference with a specific task prompt."""
if textinput:
prompt = taskprompt + textinput
else:
prompt = taskprompt
inputs = processor(
text=prompt,
images=image,
returntensors="pt"
).to("cuda", torch.float16)
generatedids = model.generate(
inputids=inputs["inputids"],
pixelvalues=inputs["pixelvalues"],
maxnewtokens=1024,
numbeams=3
)
generatedtext = processor.batchdecode(
generatedids, skipspecialtokens=False
)[0]
result = processor.postprocessgeneration(
generatedtext,
task=taskprompt,
imagesize=(image.width, image.height)
)
return result
Load a sample image
image = loadimage("https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/transformers/tasks/car.jpg")
Understanding Task Prompts
Florence-2 uses special task prompts to determine which task to perform. Here is a complete list of supported task prompts:
# Complete list of Florence-2 task prompts
TASKPROMPTS = {
# Captioning
"
": "Brief caption",
"CAPTION>": "Detailed caption",
"DETAILEDCAPTION>": "Very detailed caption",
# Object Detection
"": "Object detection",
"REGIONCAPTION>": "Caption for each detected region",
# OCR
"": "Text extraction (OCR)",
"WITHREGION>": "OCR with text region locations",
# Region Proposal
"PROPOSAL>": "Object region proposals",
# Segmentation
"EXPRESSIONSEGMENTATION>": "Segmentation based on text expression",
# Visual Grounding
"
TOPHRASEGROUNDING>": "Phrase grounding from caption",
"VOCABULARYDETECTION>": "Open-vocabulary object detection",
# Referring Expression
"TODESCRIPTION>": "Description from region",
"TOCATEGORY>": "Category from region",
"TOSEGMENTATION>": "Segmentation from region",
}
Print all task prompts
for prompt, description in TASKPROMPTS.items():
print(f"{prompt:45s} -> {description}")
Image Captioning
Florence-2 supports three levels of captioning detail.
Brief Caption
# Brief caption
result = runflorence(image, "
")
print("Caption:", result["
"])
Output: "A green car parked in front of a yellow building."
Detailed Caption
# Detailed caption
result = runflorence(image, "CAPTION>")
print("Detailed Caption:", result["CAPTION>"])
More Detailed Caption
# Very detailed caption
result = runflorence(image, "DETAILEDCAPTION>")
print("Very Detailed Caption:", result["DETAILEDCAPTION>"])
Comparing Detail Levels
# Compare all three captioning levels
for task in ["
", "CAPTION>", "DETAILEDCAPTION>"]:
result = run
florence(image, task)
print(f"\n{task}:")
print(f" {result[task]}")
Object Detection
Florence-2 can detect objects in images and produce bounding boxes along with their labels.
Standard Object Detection
# Object detection
result = runflorence(image, "")
detection = result[""]
print(f"Objects detected: {len(detection['bboxes'])}")
for bbox, label in zip(detection["bboxes"], detection["labels"]):
x1, y1, x2, y2 = bbox
print(f" {label}: [{x1:.0f}, {y1:.0f}, {x2:.0f}, {y2:.0f}]")
Dense Region Caption
# Caption for each detected region
result = runflorence(image, "REGIONCAPTION>")
regions = result["REGIONCAPTION>"]
for bbox, label in zip(regions["bboxes"], regions["labels"]):
print(f" Region: {label}")
print(f" BBox: {bbox}")
Open-Vocabulary Detection
# Open-vocabulary object detection
result = runflorence(image, "VOCABULARYDETECTION>", textinput="car. building. wheel.")
detections = result["VOCABULARYDETECTION>"]
for bbox, label in zip(detections["bboxes"], detections["labels"]):
print(f" {label}: {bbox}")
Visualizing Detection Results with Matplotlib
import matplotlib.pyplot as plt
import matplotlib.patches as patches
def visualizedetection(image, result, taskkey):
"""Visualize object detection results."""
fig, ax = plt.subplots(1, figsize=(12, 8))
ax.imshow(image)
colors = plt.cm.Set3(range(len(result[taskkey]["bboxes"])))
for idx, (bbox, label) in enumerate(
zip(result[taskkey]["bboxes"], result[taskkey]["labels"])
):
x1, y1, x2, y2 = bbox
width = x2 - x1
height = y2 - y1
rect = patches.Rectangle(
(x1, y1), width, height,
linewidth=2, edgecolor=colors[idx], facecolor="none"
)
ax.addpatch(rect)
ax.text(
x1, y1 - 5, label,
fontsize=10, color="white",
bbox=dict(boxstyle="round", facecolor=colors[idx], alpha=0.8)
)
ax.axis("off")
plt.tightlayout()
plt.savefig("detectionresult.png", dpi=150, bboxinches="tight")
plt.show()
Use the visualization function
result = runflorence(image, "")
visualizedetection(image, result, "")
OCR (Optical Character Recognition)
Florence-2 can extract text from images, both plain text and with location information.
Basic Text Extraction
# Basic OCR
result = runflorence(image, "")
print("Detected text:", result[""])
OCR with Region Information
# OCR with region locations
result = runflorence(image, "WITHREGION>")
ocrresult = result["WITHREGION>"]
for bbox, text in zip(ocrresult["quadboxes"], ocrresult["labels"]):
print(f" Text: '{text}'")
print(f" Location: {bbox}")
print()
Visualizing OCR Results
def visualizeocr(image, result):
"""Visualize OCR results with bounding boxes."""
fig, ax = plt.subplots(1, figsize=(14, 10))
ax.imshow(image)
ocrdata = result["WITHREGION>"]
for quad, label in zip(ocrdata["quadboxes"], ocrdata["labels"]):
# quadboxes contain [x1,y1, x2,y2, x3,y3, x4,y4]
xcoords = [quad[i] for i in range(0, 8, 2)]
ycoords = [quad[i] for i in range(1, 8, 2)]
polygon = patches.Polygon(
list(zip(xcoords, ycoords)),
closed=True, fill=False,
edgecolor="red", linewidth=1.5
)
ax.addpatch(polygon)
ax.text(
min(xcoords), min(ycoords) - 3, label,
fontsize=7, color="white",
bbox=dict(boxstyle="round", facecolor="red", alpha=0.7)
)
ax.axis("off")
plt.title("Florence-2 OCR Results")
plt.tightlayout()
plt.savefig("ocrresult.png", dpi=150, bboxinches="tight")
plt.show()
result = runflorence(image, "WITHREGION>")
visualizeocr(image, result)
Region Proposal and Segmentation
Region Proposal
# Generate region proposals
result = runflorence(image, "PROPOSAL>")
proposals = result["PROPOSAL>"]
print(f"Proposed regions: {len(proposals['bboxes'])}")
for i, (bbox, label) in enumerate(zip(proposals["bboxes"], proposals["labels"])):
print(f" Region {i+1}: {bbox} - {label}")
Region Description
# Get description for a specific region
Region format: x1>y1>x2>y2> (normalized coordinates 0-999)
result = runflorence(image, "TODESCRIPTION>", textinput="100>100>500>500>")
print("Region description:", result["TODESCRIPTION>"])
Region Category
# Get category for a specific region
result = runflorence(image, "TOCATEGORY>", textinput="100>100>500>500>")
print("Region category:", result["TOCATEGORY>"])
Region Segmentation
# Segmentation from a specific region
result = runflorence(image, "TOSEGMENTATION>", textinput="100>100>500>500>")
print("Region segmentation:", result["TOSEGMENTATION>"])
Visual Grounding and Referring Expression
Caption to Phrase Grounding
# Phrase grounding from a caption
caption = "A green car parked in front of a yellow building"
result = runflorence(image, "TOPHRASEGROUNDING>", textinput=caption)
grounding = result["
TOPHRASEGROUNDING>"]
for bbox, label in zip(grounding["bboxes"], grounding["labels"]):
print(f" Phrase: '{label}'")
print(f" Location: {bbox}")
Referring Expression Segmentation
# Segmentation based on text expression
result = runflorence(
image,
"EXPRESSIONSEGMENTATION>",
textinput="the green car"
)
segmentation = result["EXPRESSIONSEGMENTATION>"]
print("Segmentation polygons:", segmentation)
Visualizing Grounding Results
def visualizegrounding(image, result):
"""Visualize phrase grounding results."""
fig, ax = plt.subplots(1, figsize=(12, 8))
ax.imshow(image)
grounding = result["
TOPHRASEGROUNDING>"]
colors = ["#FF6B6B", "#4ECDC4", "#45B7D1", "#96CEB4", "#FFEAA7"]
for idx, (bbox, label) in enumerate(
zip(grounding["bboxes"], grounding["labels"])
):
color = colors[idx % len(colors)]
x1, y1, x2, y2 = bbox
rect = patches.Rectangle(
(x1, y1), x2 - x1, y2 - y1,
linewidth=2, edgecolor=color, facecolor="none"
)
ax.addpatch(rect)
ax.text(
x1, y1 - 5, label,
fontsize=11, color="white",
bbox=dict(boxstyle="round", facecolor=color, alpha=0.8)
)
ax.axis("off")
plt.title("Phrase Grounding Result")
plt.tight
layout()
plt.savefig("groundingresult.png", dpi=150, bboxinches="tight")
plt.show()
caption = "A green car parked in front of a yellow building"
result = runflorence(image, "TOPHRASEGROUNDING>", textinput=caption)
visualizegrounding(image, result)
Batch Processing
For efficient processing of many images, use batch processing.
import os
from pathlib import Path
def batchprocess(imagepaths, taskprompt, textinput=None, batchsize=4):
"""Batch process images with Florence-2."""
allresults = []
for i in range(0, len(imagepaths), batchsize):
batchpaths = imagepaths[i:i + batchsize]
batchimages = [loadimage(p) for p in batchpaths]
if textinput:
prompts = [taskprompt + textinput] len(batchimages)
else:
prompts = [taskprompt] len(batchimages)
inputs = processor(
text=prompts,
images=batchimages,
returntensors="pt",
padding=True
).to("cuda", torch.float16)
generatedids = model.generate(
inputids=inputs["inputids"],
pixelvalues=inputs["pixelvalues"],
maxnewtokens=1024,
numbeams=3
)
generatedtexts = processor.batchdecode(
generatedids, skipspecialtokens=False
)
for j, (text, img) in enumerate(zip(generatedtexts, batchimages)):
result = processor.postprocessgeneration(
text,
task=taskprompt,
imagesize=(img.width, img.height)
)
allresults.append({
"imagepath": batchpaths[j],
"result": result
})
print(f"Batch {i // batchsize + 1} completed ({len(batchpaths)} images)")
return allresults
Example batch processing usage
imagedir = "path/to/images"
imagepaths = list(Path(imagedir).glob(".jpg"))
Batch captioning
results = batchprocess(imagepaths, "
", batchsize=4)
for r in results:
print(f"{r['image
path']}: {r['result']['']}")
Multi-Task Batch Processing
def multitaskprocess(imagepath, tasks):
"""Run multiple tasks on a single image."""
image = load
image(imagepath)
results = {}
for task
prompt in tasks:
result = runflorence(image, taskprompt)
results[taskprompt] = result[taskprompt]
return results
Run multiple tasks on one image
tasks = ["
", "", ""]
results = multitaskprocess("example.jpg", tasks)
for task, result in results.items():
print(f"\n{task}: {result}")
Fine-tuning on Custom Dataset
Fine-tuning Florence-2 allows you to improve performance on specific domains.
Dataset Preparation
from datasets import Dataset
from PIL import Image
import json
def preparedataset(annotationsfile, imagesdir):
"""Prepare dataset for Florence-2 fine-tuning."""
with open(annotationsfile, "r") as f:
annotations = json.load(f)
data = {
"image": [],
"prefix": [],
"suffix": [],
}
for ann in annotations:
imagepath = os.path.join(imagesdir, ann["imagefilename"])
image = Image.open(imagepath).convert("RGB")
# For object detection task
if "objects" in ann:
prefix = ""
# Output format: label1x1>y1>x2>y2>label2...
suffixparts = []
for obj in ann["objects"]:
bbox = obj["bbox"] # [x1, y1, x2, y2] normalized
label = obj["label"]
x1 = int(bbox[0] 999)
y1 = int(bbox[1] 999)
x2 = int(bbox[2] 999)
y2 = int(bbox[3] 999)
suffixparts.append(
f"{label}{x1}>{y1}>{x2}>{y2}>"
)
suffix = "".join(suffixparts)
# For captioning task
elif "caption" in ann:
prefix = "
"
suffix = ann["caption"]
data["image"].append(image)
data["prefix"].append(prefix)
data["suffix"].append(suffix)
return Dataset.fromdict(data)
Prepare datasets
traindataset = preparedataset("trainannotations.json", "trainimages/")
valdataset = preparedataset("valannotations.json", "valimages/")
print(f"Training dataset: {len(traindataset)} samples")
print(f"Validation dataset: {len(valdataset)} samples")
Configuration and Training
from transformers import TrainingArguments, Trainer
import torch
class Florence2Dataset(torch.utils.data.Dataset):
"""Custom dataset for Florence-2 fine-tuning."""
def init(self, dataset, processor, maxlength=1024):
self.dataset = dataset
self.processor = processor
self.maxlength = maxlength
def len(self):
return len(self.dataset)
def getitem(self, idx):
item = self.dataset[idx]
image = item["image"]
prefix = item["prefix"]
suffix = item["suffix"]
inputs = self.processor(
text=prefix,
images=image,
returntensors="pt",
maxlength=self.maxlength,
padding="maxlength",
truncation=True
)
labels = self.processor.tokenizer(
suffix,
returntensors="pt",
maxlength=self.maxlength,
padding="maxlength",
truncation=True
)
return {
"inputids": inputs["inputids"].squeeze(),
"pixelvalues": inputs["pixelvalues"].squeeze(),
"labels": labels["inputids"].squeeze(),
}
Create dataset loaders
traintorchdataset = Florence2Dataset(traindataset, processor)
valtorchdataset = Florence2Dataset(valdataset, processor)
Training configuration
trainingargs = TrainingArguments(
outputdir="./florence2-finetuned",
numtrainepochs=10,
perdevicetrainbatchsize=4,
perdeviceevalbatchsize=4,
gradientaccumulationsteps=4,
learningrate=1e-5,
weightdecay=0.01,
warmupratio=0.1,
lrschedulertype="cosine",
loggingsteps=50,
evalstrategy="steps",
evalsteps=200,
savestrategy="steps",
savesteps=200,
savetotallimit=3,
loadbestmodelatend=True,
metricforbestmodel="evalloss",
fp16=True,
dataloadernumworkers=4,
removeunusedcolumns=False,
reportto="tensorboard",
)
Initialize Trainer
trainer = Trainer(
model=model,
args=trainingargs,
traindataset=traintorchdataset,
evaldataset=valtorchdataset,
)
Start training
trainer.train()
Save the model
trainer.savemodel("./florence2-finetuned-final")
processor.savepretrained("./florence2-finetuned-final")
print("Model saved successfully!")
Fine-tuning with LoRA (Memory Efficient)
from peft import LoraConfig, getpeftmodel
LoRA configuration
loraconfig = LoraConfig(
r=16,
loraalpha=32,
targetmodules=["qproj", "vproj", "kproj", "oproj"],
loradropout=0.05,
bias="none",
tasktype="CAUSALLM",
)
Apply LoRA to the model
peftmodel = getpeftmodel(model, loraconfig)
peftmodel.printtrainableparameters()
Output: trainable params: 2,359,296 || all params: 234,567,890 || trainable%: 1.01%
Use peftmodel instead of model in Trainer
trainer = Trainer(
model=peftmodel,
args=trainingargs,
traindataset=traintorchdataset,
evaldataset=valtorchdataset,
)
trainer.train()
peftmodel.savepretrained("./florence2-lora-finetuned")
Integration with Supervision for Visualization
The Supervision library from Roboflow provides powerful visualization utilities for Florence-2 detection results.
import supervision as sv
import numpy as np
def florencetosupervision(result, taskkey, imagesize):
"""Convert Florence-2 results to Supervision format."""
detectionsdata = result[taskkey]
if "bboxes" not in detectionsdata or len(detectionsdata["bboxes"]) == 0:
return sv.Detections.empty()
bboxes = np.array(detectionsdata["bboxes"])
labels = detectionsdata["labels"]
# Create unique label to classid mapping
uniquelabels = list(set(labels))
classids = np.array([uniquelabels.index(l) for l in labels])
detections = sv.Detections(
xyxy=bboxes,
classid=classids,
data={"classname": np.array(labels)}
)
return detections, uniquelabels
Object Detection Visualization
def visualizewithsupervision(image, result, taskkey=""):
"""Visualize detections using Supervision."""
image
np = np.array(image)
detections, labels = florencetosupervision(
result, taskkey, (image.width, image.height)
)
# Bounding box annotator
boxannotator = sv.BoxAnnotator(
thickness=2,
color=sv.ColorPalette.fromhex(
["#FF6B6B", "#4ECDC4", "#45B7D1", "#96CEB4", "#FFEAA7"]
)
)
# Label annotator
labelannotator = sv.LabelAnnotator(
textscale=0.5,
textthickness=1,
textpadding=5,
color=sv.ColorPalette.fromhex(
["#FF6B6B", "#4ECDC4", "#45B7D1", "#96CEB4", "#FFEAA7"]
)
)
# Apply annotations
annotated = boxannotator.annotate(
scene=imagenp.copy(),
detections=detections
)
annotated = labelannotator.annotate(
scene=annotated,
detections=detections,
labels=list(detections.data["classname"])
)
# Save result
resultimage = Image.fromarray(annotated)
resultimage.save("supervisiondetection.png")
print("Result saved to supervisiondetection.png")
return annotated
Run detection and visualize
result = runflorence(image, "")
annotated = visualizewithsupervision(image, result)
Heatmap Visualization
def visualizeheatmap(image, result, taskkey=""):
"""Create a heatmap from object detections."""
image
np = np.array(image)
detections, = florencetosupervision(
result, taskkey, (image.width, image.height)
)
heatmapannotator = sv.HeatMapAnnotator(
radius=40,
kernelsize=25,
)
heatmap = heatmapannotator.annotate(
scene=imagenp.copy(),
detections=detections
)
resultimage = Image.fromarray(heatmap)
resultimage.save("detectionheatmap.png")
return heatmap
result = runflorence(image, "")
heatmap = visualizeheatmap(image, result)
Practical Project: Document Analysis Pipeline
Here is a complete example of building a document analysis pipeline that combines OCR, captioning, and object detection.
import json
from datetime import datetime
from pathlib import Path
class DocumentAnalysisPipeline:
"""Document analysis pipeline using Florence-2."""
def init(self, modelid="microsoft/Florence-2-large"):
self.processor = AutoProcessor.frompretrained(
modelid, trustremotecode=True
)
self.model = AutoModelForCausalLM.frompretrained(
modelid,
torchdtype=torch.float16,
trustremotecode=True
).to("cuda")
self.model.eval()
print(f"Pipeline loaded with model: {modelid}")
def runtask(self, image, taskprompt, textinput=None):
"""Run a single Florence-2 task."""
prompt = taskprompt + textinput if textinput else taskprompt
inputs = self.processor(
text=prompt, images=image,
returntensors="pt"
).to("cuda", torch.float16)
with torch.nograd():
generatedids = self.model.generate(
inputids=inputs["inputids"],
pixelvalues=inputs["pixelvalues"],
maxnewtokens=1024,
numbeams=3
)
generatedtext = self.processor.batchdecode(
generatedids, skipspecialtokens=False
)[0]
return self.processor.postprocessgeneration(
generatedtext,
task=taskprompt,
imagesize=(image.width, image.height)
)
def extracttext(self, image):
"""Extract text from a document."""
result = self.runtask(image, "WITHREGION>")
ocrdata = result["WITHREGION>"]
textblocks = []
for quad, text in zip(ocrdata["quadboxes"], ocrdata["labels"]):
textblocks.append({
"text": text,
"position": {
"x1": quad[0], "y1": quad[1],
"x2": quad[2], "y2": quad[3],
"x3": quad[4], "y3": quad[5],
"x4": quad[6], "y4": quad[7],
}
})
# Sort top-to-bottom, left-to-right
textblocks.sort(key=lambda b: (b["position"]["y1"], b["position"]["x1"]))
fulltext = " ".join([b["text"] for b in textblocks])
return {"fulltext": fulltext, "textblocks": textblocks}
def generatedescription(self, image):
"""Generate a detailed description of the document."""
result = self.runtask(image, "DETAILEDCAPTION>")
return result["DETAILEDCAPTION>"]
def detectelements(self, image):
"""Detect elements in the document."""
result = self.runtask(image, "")
detection = result[""]
elements = []
for bbox, label in zip(detection["bboxes"], detection["labels"]):
elements.append({
"type": label,
"bbox": {
"x1": bbox[0], "y1": bbox[1],
"x2": bbox[2], "y2": bbox[3]
},
"area": (bbox[2] - bbox[0]) (bbox[3] - bbox[1])
})
return elements
def detectspecificelements(self, image, elementtypes):
"""Detect specific elements using open-vocabulary detection."""
textinput = ". ".join(elementtypes) + "."
result = self.runtask(
image, "VOCABULARYDETECTION>", textinput=textinput
)
return result["VOCABULARYDETECTION>"]
def analyzedocument(self, imagepath, outputdir="analysisoutput"):
"""Run complete analysis on a single document."""
os.makedirs(outputdir, existok=True)
image = loadimage(imagepath)
print(f"Analyzing: {imagepath}")
print(f"Image size: {image.width}x{image.height}")
results = {}
# Step 1: Extract text
print("\n[1/4] Extracting text...")
textresult = self.extracttext(image)
results["text"] = textresult
print(f" Text found: {len(textresult['textblocks'])} blocks")
print(f" Preview: {textresult['fulltext'][:200]}...")
# Step 2: Document description
print("\n[2/4] Generating description...")
description = self.generatedescription(image)
results["description"] = description
print(f" Description: {description[:200]}...")
# Step 3: Element detection
print("\n[3/4] Detecting elements...")
elements = self.detectelements(image)
results["elements"] = elements
print(f" Elements detected: {len(elements)}")
for elem in elements:
print(f" - {elem['type']} (area: {elem['area']:.0f}px)")
# Step 4: Specific document element detection
print("\n[4/4] Detecting specific document elements...")
docelements = self.detectspecificelements(
image,
["table", "chart", "logo", "signature", "header", "paragraph"]
)
results["documentelements"] = docelements
# Save results
timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
outputfile = os.path.join(outputdir, f"analysis{timestamp}.json")
with open(outputfile, "w", encoding="utf-8") as f:
json.dump(results, f, indent=2, ensureascii=False)
print(f"\nResults saved to: {outputfile}")
# Create visualization