10. Medical Imaging Analysis

Chapter 10 of 18 · 20 min

Medical imaging consumes significant radiology capacity. Chest X-rays, CT scans, and MRI studies require expert interpretation. Local AI models can assist by identifying findings, prioritizing worklists, and drafting preliminary reports.

Vision-language models (VLMs) handle imaging analysis by accepting images as input alongside text prompts. Ollama supports vision-capable models like Llama 3.2 Vision and specialized medical models.

# Install vision model for medical imaging
ollama pull llama3.2-vision

# Verify vision capability
ollama show llama3.2-vision
# medical_image_analyzer.py
import base64
from typing import Optional, List, Dict
from dataclasses import dataclass

@dataclass
class ImagingFinding:
    finding: str
    location: str
    severity: str
    description: str

class MedicalImageAnalyzer:
    """Analyze medical images using local vision-language model."""
    
    def __init__(self, ollama_client, model: str = "llama3.2-vision"):
        self.ollama = ollama_client
        self.model = model
        
    def analyze_chest_xray(self, image_path: str) -> Dict:
        """Analyze chest X-ray for common findings."""
        
        image_data = self._load_image_base64(image_path)
        
        prompt = """Analyze this chest X-ray and identify:
        1. Cardiomegaly
        2. Pneumonia/consolidation
        3. Pleural effusion
        4. Pneumothorax
        5. Pulmonary edema
        6. Lung nodules or masses
        
        For each finding present, describe location, severity, and key features.
        If normal, state "No acute cardiopulmonary abnormality identified."
        
        Output as structured JSON."""
        
        response = self.ollama.chat(
            model=self.model,
            messages=[{
                "role": "user",
                "content": prompt,
                "images": [image_data]
            }]
        )
        
        return self._parse_analysis_response(response["message"]["content"])
    
    def analyze_ct_head(self, image_path: str) -> Dict:
        """Analyze head CT for acute findings."""
        
        image_data = self._load_image_base64(image_path)
        
        prompt = """Analyze this head CT for acute findings:
        1. Intracranial hemorrhage
        2. Ischemic stroke signs
        3. Mass effect or midline shift
        4. Hydrocephalus
        5. Skull fracture
        
        Prioritize findings that require immediate clinical attention.
        Output as structured JSON with severity ratings."""
        
        response = self.ollama.chat(
            model=self.model,
            messages=[{
                "role": "user",
                "content": prompt,
                "images": [image_data]
            }]
        )
        
        return self._parse_analysis_response(response["message"]["content"])
    
    def prioritize_worklist(self, image_paths: List[str]) -> List[Dict]:
        """Prioritize imaging studies by likely urgency."""
        prioritized = []
        
        for path in image_paths:
            analysis = self.analyze_chest_xray(path)
            # Assign priority score based on findings
            priority = self._calculate_priority(analysis)
            prioritized.append({
                "path": path,
                "priority": priority,
                "findings": analysis
            })
        
        # Sort by priority descending
        prioritized.sort(key=lambda x: x["priority"], reverse=True)
        return prioritized
    
    def _load_image_base64(self, image_path: str) -> str:
        """Load image and convert to base64 for API."""
        with open(image_path, "rb") as f:
            return base64.b64encode(f.read()).decode("utf-8")
    
    def _parse_analysis_response(self, response: str) -> Dict:
        """Parse LLM analysis into structured format."""
        import json
        # Try JSON parsing first
        try:
            return json.loads(response)
        except:
            # Return as text if parsing fails
            return {"raw_analysis": response, "findings": []}
    
    def _calculate_priority(self, analysis: Dict) -> float:
        """Calculate urgency priority score."""
        critical_findings = [
            "pneumothorax", "hemorrhage", "acute stroke",
            "tension pneumothorax", "midline shift"
        ]
        
        findings = analysis.get("findings", [])
        for finding in findings:
            if any(cf in str(finding).lower() for cf in critical_findings):
                return 1.0
        
        # Check severity field
        severity = analysis.get("severity", "low")
        severity_map = {"critical": 1.0, "high": 0.8, "moderate": 0.5, "low": 0.2}
        return severity_map.get(severity, 0.3)

Medical imaging analysis faces specific challenges: subtle findings that distinguish normal from abnormal require fine-tuned models. General-purpose vision models perform reasonably on obvious pathology but miss subtle abnormalities. Consider fine-tuning or using medically-specialized models for production systems.

EXERCISE

Collect ten chest X-rays with known findings. Run analysis on each and compare to ground truth. Calculate sensitivity for each finding type and identify systematic weaknesses.