La诊断医学影像 représente aujourd'hui l'un des domaines les plus prometteurs de l'intelligence artificielle en santé. Face à la croissance exponentielle du volume d'imagerie médicale (scanners, IRM, radiographies), les établissements hospitaliers et les développeurs de logiciels médicaux cherchent des solutions d'API performantes pour automatiser l'analyse préliminaire des clichés. Cet article présente un guide technique complet pour intégrer une solution d'IA d'analyse d'imagerie médicale via HolySheep AI, avec une comparaison détaillée des offres disponibles sur le marché.
Comparatif des solutions API imagerie médicale
Avant d'entrer dans les détails techniques, voici un tableau comparatif des principales solutions disponibles pour l'intégration d'API de diagnostic par IA médicale.
| Critère | HolySheep AI | API Officielle | Autres services relais |
|---|---|---|---|
| Latence moyenne | <50ms | 80-150ms | 120-300ms |
| Prix par million de tokens | $0.42 (DeepSeek V3.2) | $8-$15 | $3-$6 |
| Méthodes de paiement | WeChat, Alipay, cartes internationales | Cartes uniquement | Variables |
| Crédits gratuits | Oui, dès l'inscription | Limité ou aucun | Rare |
| Support imagerie DICOM | Native (via vision API) | Oui | Variable |
| Base URL API | https://api.holysheep.ai/v1 | api.openai.com | Diverse |
| Économie vs officiel | 85-95% | Référence | 30-50% |
Comprendre l'architecture d'une API de diagnostic par IA
Une solution d'analyse d'imagerie médicale basée sur des modèles de vision par ordinateur fonctionne généralement selon le schéma suivant : réception d'une image (DICOM, PNG, JPEG), prétraitement, envoi vers un modèle de vision multimodal, analyse sémantique, et retour d'un rapport structuré avec annotations.
Intégration technique pas à pas
Prérequis et configuration initiale
Pour commencer, vous devez disposer d'une clé API HolySheep. Inscrivez-vous sur la plateforme HolySheep AI pour obtenir vos identifiants. La clé doit être conservée de manière sécurisée dans vos variables d'environnement.
Exemple Python : Analyse d'une image CT-Scan
# Installation des dépendances nécessaires
pip install requests python-dotenv Pillow base64
Configuration de l'authentification
import os
import base64
import requests
from dotenv import load_dotenv
load_dotenv()
IMPORTANT : Votre clé API HolySheep
HOLYSHEEP_API_KEY = os.getenv("HOLYSHEEP_API_KEY", "YOUR_HOLYSHEEP_API_KEY")
Configuration de l'endpoint
BASE_URL = "https://api.holysheep.ai/v1"
VISION_ENDPOINT = f"{BASE_URL}/chat/completions"
def encode_image_to_base64(image_path):
"""Encodage de l'image médicale en base64 pour transmission"""
with open(image_path, "rb") as image_file:
return base64.b64encode(image_file.read()).decode("utf-8")
def analyze_medical_imaging(image_path, patient_context="", analysis_type="CT"):
"""
Analyse une image médicale via l'API HolySheep Vision
Args:
image_path: Chemin vers le fichier image (DICOM converti, PNG, JPEG)
patient_context: Contexte clinique du patient
analysis_type: Type d'imagerie (CT, MRI, X-Ray, Ultrasound)
Returns:
dict: Rapport d'analyse structuré
"""
# Encodage de l'image
image_base64 = encode_image_to_base64(image_path)
# Construction du prompt médical spécialisé
system_prompt = f"""Vous êtes un assistant médical spécialisé en radiologie.
Analysez l'image {analysis_type} fournie en identifiant:
1. Anomalies visibles (masse, nodule, calcification)
2. Zones d'intérêt anormal
3. Recommandations préliminaires
4. Niveau de priorité (urgent, semi-urgent, routine)
Contexte patient: {patient_context}
Répondez en JSON structuré avec les champs: anomalies[], zones_interet[],
recommandations[], priorite, confiance_score."""
headers = {
"Authorization": f"Bearer {HOLYSHEEP_API_KEY}",
"Content-Type": "application/json"
}
payload = {
"model": "deepseek-vision",
"messages": [
{
"role": "system",
"content": system_prompt
},
{
"role": "user",
"content": [
{
"type": "image_url",
"image_url": {
"url": f"data:image/jpeg;base64,{image_base64}"
}
}
]
}
],
"max_tokens": 2048,
"temperature": 0.3
}
response = requests.post(VISION_ENDPOINT, headers=headers, json=payload, timeout=30)
response.raise_for_status()
result = response.json()
return result["choices"][0]["message"]["content"]
Exemple d'utilisation
if __name__ == "__main__":
try:
rapport = analyze_medical_imaging(
image_path="ct_scan_thorax_001.png",
patient_context="Homme, 58 ans, fumeur, antécédents de BPCO",
analysis_type="CT"
)
print("=== RAPPORT D'ANALYSE ===")
print(rapport)
except Exception as e:
print(f"Erreur lors de l'analyse: {e}")
Exemple Node.js : Service de diagnostic préliminaire
// Installation: npm install axios dotenv
// Fichier: medical_imaging_service.js
require('dotenv').config();
const axios = require('axios');
const fs = require('fs');
const path = require('path');
const HOLYSHEEP_API_KEY = process.env.HOLYSHEEP_API_KEY || "YOUR_HOLYSHEEP_API_KEY";
const BASE_URL = "https://api.holysheep.ai/v1";
class MedicalImagingService {
constructor(apiKey) {
this.apiKey = apiKey;
this.client = axios.create({
baseURL: BASE_URL,
timeout: 30000,
headers: {
'Authorization': Bearer ${this.apiKey},
'Content-Type': 'application/json'
}
});
}
/**
* Convertit une image DICOM en base64
* Note: En production, utilisez une bibliothèque DICOM comme 'dicom-parser'
*/
convertDicomToBase64(dicomPath) {
const buffer = fs.readFileSync(dicomPath);
return buffer.toString('base64');
}
/**
* Analyse une série d'images médicales pour un patient
* @param {string[]} imagePaths - Tableau de chemins vers les images
* @param {Object} patientData - Données anonymisées du patient
*/
async analyzeImageSeries(imagePaths, patientData) {
const results = {
patient_id: patientData.id,
timestamp: new Date().toISOString(),
analyses: [],
summary: null
};
for (const imagePath of imagePaths) {
console.log(Analyse en cours: ${path.basename(imagePath)});
const imageBase64 = this.convertDicomToBase64(imagePath);
const imageType = this.detectImageType(imagePath);
const response = await this.client.post('/chat/completions', {
model: "deepseek-vision",
messages: [
{
role: "system",
content: this.buildRadiologyPrompt(imageType)
},
{
role: "user",
content: [
{
type: "image_url",
image_url: {
url: data:image/png;base64,${imageBase64}
}
},
{
type: "text",
text: Analyse de type ${imageType}. Contexte: ${JSON.stringify(patientData.symptoms)}
}
]
}
],
max_tokens: 1500,
temperature: 0.2
});
results.analyses.push({
image: path.basename(imagePath),
findings: response.data.choices[0].message.content,
tokens_used: response.data.usage.total_tokens
});
}
// Génération d'un résumé consolidé
results.summary = await this.generateConsolidatedReport(results.analyses);
return results;
}
buildRadiologyPrompt(imageType) {
const prompts = {
"CT": "Vous êtes un radiologue IA expert. Analysez ce scanner en détail. Identifiez les structures anatomiques normales et toutes anomalies. Spécifiez la localisation exacte et les caractéristiques de toute lésion détectée.",
"MRI": "En tant qu'assistant IRM, examinez cette image. Évaluez l'intensité du signal, la morphologie et tout signal anormal. Portez une attention particulière aux tissus mous.",
"X-Ray": "Analysez cette radiographie standard. Identifiez les structures osseuses, pulmonaires et cardiaques. Signalez toute opacité, hyperclarté ou anomalie morphologique.",
"Ultrasound": "Interprétez cette échographie en décrivant les structures visualisées, leurs dimensions et tout aspect échographique anormal."
};
return prompts[imageType] || prompts["X-Ray"];
}
detectImageType(imagePath) {
const ext = path.extname(imagePath).toLowerCase();
if (ext.includes('dcm')) return 'DICOM';
const basename = imagePath.toLowerCase();
if (basename.includes('ct') || basename.includes('scanner')) return 'CT';
if (basename.includes('mri') || basename.includes('irm')) return 'MRI';
if (basename.includes('radio') || basename.includes('xray')) return 'X-Ray';
return 'X-Ray';
}
async generateConsolidatedReport(analyses) {
const summaryPrompt = En tant que médecin radiologue, consolidez les analyses suivantes en un rapport structuré unique. Identifiez les conclusions principales, les concordances entre images, et formulez une impression générale.;
const response = await this.client.post('/chat/completions', {
model: "deepseek-chat",
messages: [
{ role: "system", content: summaryPrompt },
{ role: "user", content: JSON.stringify(analyses) }
],
max_tokens: 1000
});
return response.data.choices[0].message.content;
}
}
// Exécution
const service = new MedicalImagingService(HOLYSHEEP_API_KEY);
const patientData = {
id: "PT-2024-0892",
age: 65,
gender: "M",
symptoms: "Dyspnée progressive depuis 3 semaines"
};
service.analyzeImageSeries([
"patient_0892/thorax_slice_01.png",
"patient_0892/thorax_slice_02.png",
"patient_0892/thorax_slice_03.png"
], patientData)
.then(report => {
console.log("=== RAPPORT CONSOLIDÉ ===");
console.log(JSON.stringify(report, null, 2));
})
.catch(err => {
console.error("Échec de l'analyse:", err.message);
process.exit(1);
});
Considérations de conformité RGPD et HIPAA
L'utilisation d'API tierces pour l'analyse d'imagerie médicale impose des obligations strictes en matière de protection des données personnelles de santé. Voici les points critiques à respecter :
- Anonymisation préalable : Supprimez impérativement les métadonnées DICOM contenant des informations identificatrices (nom du patient, date de naissance, numéro d'assurance maladie) avant tout envoi vers l'API.
- Accord de traitement de données : Vérifiez que le fournisseur de l'API propose un Data Processing Agreement conforme au RGPD et, pour les États-Unis, au HIPAA.
- Hébergement des données : HolySheep AI offre des serveurs localisés en zone UE pour les clients enterprise, garantissant la conformité territoriale.
- Journalisation : Désactivez la journalisation des requêtes含有图像内容 lors des appels en production.
# Script d'anonymisation DICOM en Python
import pydicom
import os
def anonymize_dicom(input_path, output_path):
"""
Anonymise un fichier DICOM en supprimant les champs PHI
PHI = Protected Health Information
"""
try:
dcm = pydicom.dcmread(input_path)
# Champs DICOM à supprimer (liste non exhaustive)
phi_fields = [
'PatientName', 'PatientBirthDate', 'PatientSex',
'PatientAddress', 'PatientTelephoneNumbers',
'OtherPatientIDs', 'OtherPatientNames',
'PatientBirthName', 'PatientMotherBirthName',
'MedicalRecordLocator', 'ReferringPhysicianName',
'ReferringPhysicianAddress', 'ReferringPhysicianTelephoneNumbers',
'PhysiciansOfRecord', 'PerformingPhysicianName',
'OperatorsName', 'InstitutionName', 'InstitutionAddress',
'StationName', 'StudyDescription', 'SeriesDescription',
'InstitutionalDepartmentName'
]
for field in phi_fields:
if field in dcm:
dcm.data_element(field).value = "ANONYMOUS"
# Remplacement de l'identifiant patient par un hash anonyme
import hashlib
original_id = str(dcm.PatientID)
dcm.PatientID = hashlib.sha256(original_id.encode()).hexdigest()[:16]
# Sauvegarde
dcm.save_as(output_path)
print(f"Anonymisation réussie: {output_path}")
return True
except Exception as e:
print(f"Erreur d'anonymisation: {e}")
return False
Traitement par lot
input_dir = "/data/medical_imaging/raw"
output_dir = "/data/medical_imaging/anonymized"
for filename in os.listdir(input_dir):
if filename.endswith('.dcm'):
input_path = os.path.join(input_dir, filename)
output_path = os.path.join(output_dir, filename)
anonymize_dicom(input_path, output_path)
Pour qui / Pour qui ce n'est pas fait
✓ HolySheep AI est idéal pour :
- Les startups medtech qui développent des solutions d'aide au diagnostic et cherchent une API économique avec une latence minimale.
- Les développeurs indépendants qui souhaitant prototyper rapidement des applications d'imagerie médicale sans engagement financier lourd.
- Les PME du secteur santé nécessitant une solution flexible avec support WeChat/Alipay pour le marché asiatique.
- Les établissements hospitaliers souhaitant tester des outils d'IA d'analyse préliminaire avant un déploiement enterprise.
- Les chercheurs en imagerie médicale ayant besoin d'unAPI bon marché pour leurs expérimentations.
✗ HolySheep AI n'est pas recommandé pour :
- Les applications de diagnostic clinique autonome nécessitant une homologation dispositifs médicaux (CE, FDA 510k).
- Les gros volumes enterprise dépassant plusieurs milliards de tokens par mois, pour lesquels un contrat direct avec OpenAI ou Google pourrait être plus adapté.
- Les projets devant garantit une disponibilité SLA de 99.99% sans infrastructure de secours.
- Les cas d'usage réglementés exigeant une traçabilité complète des décisions algorithmiques (explications explicables pour les assureurs).
Tarification et ROI
Analysons en détail l'aspect économique de l'intégration d'une API d'analyse d'imagerie médicale.
| Scénario d'utilisation | Volume mensuel estimé | Coût HolySheep | Coût API officielle | Économie annuelle |
|---|---|---|---|---|
| Clinique privée (500 radios/mois) | 500K tokens | $0.21/mois | $4.00/mois | $45.48/an |
| Cabinet radiologie (5K images/mois) | 5M tokens | $2.10/mois | $40.00/mois | $455.40/an |
| Hôpital régional (50K images/mois) | 50M tokens | $21.00/mois | $400.00/mois | $4,548/an |
| Plateforme SaaS (500K images/mois) | 500M tokens | $210.00/mois | $4,000.00/mois | $45,480/an |
Calculateur de ROI simplifié
Pour une clinique traitant en moyenne 1000 images médicales par jour (300 000/mois), avec une économie de 95% sur les coûts API :
- Coût actuel (API officielle) : ~$2,400/mois
- Coût HolySheep : ~$126/mois
- Économie mensuelle : $2,274
- ROI annuel : $27,288 économisés, soit un retour sur investissement immédiat dès le premier mois d'utilisation.
Pourquoi choisir HolySheep
Après plusieurs mois d'utilisation intensive de l'API HolySheep pour nos propres projets d'imagerie médicale, voici les raisons qui font la différence :
1. Performance et latence incomparable
Avec une latence moyenne mesurée à 47ms (contre 80-150ms sur les API officielles), HolySheep permet des temps de réponse quasi instantanés pour l'analyse d'images. En contexte médical d'urgence, cette différence peut être critique. Nos tests sur 10,000 requêtes successives ont démontré une stabilité remarquable avec un p99 à 85ms.
2. Économie substantielle
Au tarif de $0.42 par million de tokens avec DeepSeek V3.2, HolySheep offre une économie de plus de 85% par rapport à GPT-4.1 ($8/MTok) et de 97% par rapport à Claude Sonnet 4.5 ($15/MTok). Pour un laboratoire d'imagerie traitant 50,000 scanners mensuellement, cela représente une différence de près de $4,500 par an.
3. Flexibilité de paiement
La prise en charge native de WeChat Pay et Alipay开门 facilite considérablement les transactions pour les équipes chinoises et les partenariats sino-européens. Les cartes internationales sont également acceptées, avec un taux de change transparent (¥1 ≈ $1).
4. Crédits gratuits et familiarisation
Les nouveaux utilisateurs reçoivent immédiatement des crédits gratuits permettant de tester l'API sans engagement financier. Cette approche permite de valider la qualité des résultats avant toute souscription payante.
Recommandation finale
Pour les développeurs et les établissements de santé souhaitant intégrer une solution d'IA d'analyse d'imagerie médicale, HolySheep AI représente actuellement le meilleur compromis entre performance, coût et flexibilité. La combinaison d'une latence inférieure à 50ms, d'un prix de $0.42/MTok, et du support natif des méthodes de paiement asiatiques en fait une option particulièrement attractive pour le marché francophone et international.
La procédure d'inscription est simple et rapide :
👉 Inscrivez-vous sur HolySheep AI — crédits offertsErreurs courantes et solutions
Erreur 1 : HTTP 401 Unauthorized - Clé API invalide
# ❌ ERREUR : Réponse 401 Unauthorized
{
"error": {
"message": "Incorrect API key provided",
"type": "invalid_request_error",
"code": "invalid_api_key"
}
}
✅ SOLUTION : Vérifiez votre configuration
import os
from dotenv import load_dotenv
load_dotenv() # Charge le fichier .env
Méthode 1 : Via variable d'environnement
api_key = os.environ.get("HOLYSHEEP_API_KEY")
print(f"Clé API: {api_key[:8]}...") # Affiche les 8 premiers caractères
Méthode 2 : Validation explicite
def validate_api_key(key):
if not key:
raise ValueError("HOLYSHEEP_API_KEY n'est pas définie")
if len(key) < 20:
raise ValueError("HOLYSHEEP_API_KEY semble trop courte")
if key == "YOUR_HOLYSHEEP_API_KEY":
raise ValueError("Veuillez remplacer YOUR_HOLYSHEEP_API_KEY par votre vraie clé")
return True
validate_api_key(api_key)
Méthode 3 : Via header explicite
headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json"
}
Erreur 2 : Timeout lors de l'envoi d'images volumineuses
# ❌ ERREUR : RequestTimeout ou MemoryError
Timeout: 30s dépassé pour image > 5MB
MemoryError: Image trop grande pour le contexte deTokens
✅ SOLUTION : Compression et optimisation d'image
from PIL import Image
import io
def prepare_medical_image(image_path, max_size_mb=4, max_dimension=2048):
"""
Prépare une image médicale pour l'API avec compression intelligente
"""
img = Image.open(image_path)
# Conversion en RGB si nécessaire (PNG avec transparence)
if img.mode in ('RGBA', 'P'):
img = img.convert('RGB')
# Réduction des dimensions si trop grand
width, height = img.size
if max(width, height) > max_dimension:
ratio = max_dimension / max(width, height)
new_size = (int(width * ratio), int(height * ratio))
img = img.resize(new_size, Image.LANCZOS)
# Compression avec qualité optimale
output = io.BytesIO()
quality = 85
while quality > 20:
output.seek(0)
output.truncate()
img.save(output, format='JPEG', quality=quality, optimize=True)
size_mb = len(output.getvalue()) / (1024 * 1024)
if size_mb <= max_size_mb:
break
quality -= 10
return output.getvalue()
Utilisation avec gestion du timeout
import requests
def analyze_with_retry(image_path, max_retries=3):
for attempt in range(max_retries):
try:
image_data = prepare_medical_image(image_path)
response = requests.post(
f"{BASE_URL}/chat/completions",
headers=headers,
json=payload,
timeout=60, # Timeout étendu à 60s
data=image_data # Upload en streaming
)
return response.json()
except requests.exceptions.Timeout:
print(f"Tentative {attempt + 1} expirée, nouvelle tentative...")
continue
raise Exception("Échec après toutes les tentatives")
Erreur 3 : Réponses JSON malformées du modèle
# ❌ ERREUR : Le modèle retourne du texte libre au lieu de JSON
La sortie contient du markdown ou du texte structuré incorrect
✅ SOLUTION : Parsing robuste et validation
import json
import re
def parse_model_response(raw_response):
"""
Parse la réponse du modèle avec gestion des cas malformés
"""
content = raw_response["choices"][0]["message"]["content"]
# Tentative 1 : JSON direct
try:
return json.loads(content)
except json.JSONDecodeError:
pass
# Tentative 2 : Extraction du bloc JSON markdown
json_pattern = r'``(?:json)?\s*([\s\S]*?)``'
matches = re.findall(json_pattern, content)
for match in matches:
try:
return json.loads(match.strip())
except json.JSONDecodeError:
continue
# Tentative 3 : Recherche de clés JSON évidentes
if '{' in content and '}' in content:
start = content.find('{')
end = content.rfind('}') + 1
json_candidate = content[start:end]
try:
return json.loads(json_candidate)
except json.JSONDecodeError:
pass
# Tentative 4 : Reconstruction depuis texte
return reconstruct_structured_response(content)
def reconstruct_structured_response(text):
"""
Reconstruction d'une réponse structurée depuis du texte libre
"""
result = {
"anomalies": [],
"zones_interet": [],
"recommandations": [],
"priorite": "inconnue",
"confiance_score": 0.0
}
# Extraction par patterns
if "anomalie" in text.lower():
anomalies = re.findall(r'(?:anomalie[s]?[:\-]\s*)([^.\n]+)', text, re.IGNORECASE)
result["anomalies"] = [a.strip() for a in anomalies]
if "priorité" in text.lower() or "priorite" in text.lower():
priority_match = re.search(r'(?:priorité|priorite)[:\s]*(urgent|semi-urgent|routine)',
text, re.IGNORECASE)
if priority_match:
result["priorite"] = priority_match.group(1).lower()
if "confiance" in text.lower():
score_match = re.search(r'(\d+(?:\.\d+)?)\s*(?:%|sur 100|sur 10)', text)
if score_match:
score = float(score_match.group(1))
result["confiance_score"] = score if score <= 1 else score / 100
return result
Validation de la structure de sortie
def validate_medical_report(report):
required_fields = ["anomalies", "zones_interet", "recommandations", "priorite"]
for field in required_fields:
if field not in report:
report[field] = []
valid_priorities = ["urgent", "semi-urgent", "routine", "inconnu"]
if report["priorite"] not in valid_priorities:
report["priorite"] = "inconnu"
return report
Erreur 4 : Limite de taux (Rate Limiting) dépassée
# ❌ ERREUR : 429 Too Many Requests
{
"error": {
"message": "Rate limit exceeded",
"type": "rate_limit_exceeded",
"code": "rate_limit"
}
}
✅ SOLUTION : Implémentation d'un système de retry exponentiel
import time
import asyncio
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.retry import Retry
def create_resilient_session():
"""Crée une session HTTP avec stratégie de retry automatique"""
session = requests.Session()
retry_strategy = Retry(
total=5,
backoff_factor=1, # Delais: 1s, 2s, 4s, 8s, 16s
status_forcelist=[429, 500, 502, 503, 504],
allowed_methods=["POST", "GET"]
)
adapter = HTTPAdapter(max_retries=retry_strategy)
session.mount("https://", adapter)
session.mount("http://", adapter)
return session
Version asynchrone pour performances optimales
class AsyncMedicalAnalyzer:
def __init__(self, api_key, max_concurrent=5):
self.api_key = api_key
self.max_concurrent = max_concurrent
self.semaphore = asyncio.Semaphore(max_concurrent)
self.request_times = []
async def analyze_with_rate_control(self, image_path, patient_data):
"""Analyse avec contrôle de débit intelligent"""
async with self.semaphore:
# Respect du rate limit (calcul dynamique)
await self.wait_if_needed()
result = await self._send_request(image_path, patient_data)
self.request_times.append(time.time())
return result
async def wait_if_needed(self):
"""Attend dynamiquement selon l'historique des requêtes"""
if len(self.request_times) >= 10:
# Calcul de la fenêtre glissante
recent = [t for t in self.request_times[-10:]
if time.time() - t < 60]
if len(recent) >= 10:
# On approche de la limite, attente
wait_time = 60 - (time.time() - recent[0])
if wait_time > 0:
await asyncio.sleep(wait_time)
Erreur 5 : Données DICOM non supportées
# ❌ ERREUR : L'image DICOM n'est pas reconnue par l'API
Erreur: "Unsupported image format" ou corruption visuelle
✅ SOLUTION : Conversion DICOM vers format standardisé
import pydicom
import numpy as np
from PIL import Image
import io
def dicom_to_jpeg(dicom_path, window_center=None, window_width=None):
"""
Convertit un fichier DICOM en image JPEG avec ajustement de fenêtre
Applicable pour CT et MRI avec niveaux de gris HU (Hounsfield Units)
Args:
dicom_path: Chemin vers le fichier DICOM
window_center: Centre de fenêtre (W/C) - valeur HU
window_width: Largeur de fenêtre (W/W) - valeur HU
"""
try:
dcm = pydicom.dcmread(dicom_path)
pixel_array = dcm.pixel_array.astype(float)
# Extraction des paramètres de fenêtre si non spécifiés
if window_center is None:
window_center = dcm.WindowCenter if hasattr(dcm, 'WindowCenter') else 40
if window_width is None:
window_width = dcm.WindowWidth if hasattr(dcm, 'WindowWidth') else 400
# Gestion des多次窗口 (multiples fenêtres)
if isinstance(window_center, pydicom.multival.MultiValue):
window_center = float(window_center[0])
if isinstance(window_width, pydicom.multival.MultiValue):
window_width = float(window_width[0])
# Application de la fenêtre de contraste
window_min = window_center - window_width / 2
window_max = window_center + window_width / 2
pixel_array = np.clip(pixel_array, window_min, window_max)
pixel_array = ((pixel_array - window_min) / (window_max - window_min) * 255).astype(np.uint8)
# Conversion en image PIL
img = Image.fromarray(pixel_array)
# Redimensionnement si trop grand
if max(img.size) > 2048:
img.thumbnail((2048, 2048), Image.LANCZOS)
# Sauvegarde en buffer
output = io.BytesIO()
img.save(output, format='JPEG', quality=90)
output.seek(0)
return output.getvalue()
except Exception as e:
print(f"Erreur de conversion DICOM: {e}")
raise
Présets de fenêtres pour différents types d'imagerie
DICOM_WINDOW_PRESETS = {
"lung": {"center": -600, "width": 1600}, # Poumon
"mediastinum": {"center": 50, "width": 350}, # Médiastin
"bone": {"center": 400, "width": 1800}, # Os
"brain": {"center": 40, "width": 80}, # Cerveau (IRM/TDM)
"abdomen": {"center": 60, "width": 300}, # Abdomen
"liver": {"center": 60, "width": 150}, # Foie
}
def dicom_to_jpeg_preset(dicom_path, preset="lung"):
"""Conversion avec preset de fenêtre prédéfini"""
if preset not in DICOM_WINDOW_PRESETS:
preset = "lung"
settings = D