Views
No views yet
snuh/mvl-rrg-1.0⚠️ These benchmarks are provided for research purposes only and do not imply clinical safety or efficacy.
⚠️ This model is intended for research and educational purposes only and should not be used to make clinical decisions.
1from transformers import Qwen3VLForConditionalGeneration, AutoProcessor
2import torch
3from pathlib import Path
4import os
5from PIL import Image
6
7# Load processor and model
8model_name = "Qwen3VL_SNUH"
9
10model = Qwen3VLForConditionalGeneration.from_pretrained(
11 model_name,
12 torch_dtype=torch.bfloat16,
13 device_map="auto"
14)
15processor = AutoProcessor.from_pretrained(model_name)
16
17# Image paths
18current_frontal_image_path = "/**/current_frontal_image.png"
19current_lateral_image_path = "/**/current_lateral_image.png"
20prior_frontal_image_path = "/**/prior_frontal_image.png"
21
22# Validate image paths exist
23if current_frontal_image_path and not Path(current_frontal_image_path).exists():
24 raise FileNotFoundError(f"Current frontal image file not found: {current_frontal_image_path}")
25if current_lateral_image_path and not Path(current_lateral_image_path).exists():
26 raise FileNotFoundError(f"Current lateral image file not found: {current_lateral_image_path}")
27if prior_frontal_image_path and not Path(prior_frontal_image_path).exists():
28 raise FileNotFoundError(f"Prior frontal image file not found: {prior_frontal_image_path}")
29
30# Clinical context
31prior_findings = "N/A"
32prior_impression = "Developed pleural effusion, both\nInterval increased nodular opacity at LMLF"
33indication = "F with chest pain // ?pna"
34technique = "CHEST (PA AND LAT)"
35comparison = "__."
36time_interval = "1 month"
37
38# Style attributes
39findings_structure_type = "narrative_paragraph"
40findings_temporal_comparison = "absent"
41findings_sentence_count = 6
42impression_structure_type = "narrative_paragraph"
43impression_temporal_comparison = "absent"
44impression_sentence_count = 1
45
46# Instruction
47inputs_list = ["- Current frontal image: <image>"]
48if current_lateral_image_path:
49 inputs_list.append("- Current lateral image: <image>")
50else:
51 inputs_list.append("- Current lateral image: N/A")
52if prior_frontal_image_path:
53 inputs_list.append("- Prior frontal image: <image>")
54else:
55 inputs_list.append("- Prior frontal image: N/A")
56inputs_list.extend([
57 f"- Prior findings: {prior_findings}",
58 f"- Prior impression: {prior_impression}"
59])
60inputs_text = "\n".join(inputs_list)
61
62instruction = f"""You are an expert radiology assistant for chest X-ray (CXR) interpretation.
63
64Inputs:
65{inputs_text}
66
67Clinical context:
68- INDICATION: {indication}
69- TECHNIQUE: {technique}
70- COMPARISON: {comparison}
71- TIME INTERVAL: {time_interval}
72 (Time elapsed between the prior study date and the current study date)
73
74Instructions:
751. Generate a chest X-ray report based on the current study.
762. Write a Findings section describing radiographic observations using standard clinical language.
773. Write an Impression section summarizing the key findings or overall assessment.
784. When applicable, include conditions related to CheXbert classes
79 (e.g., cardiomegaly, lung opacity, pleural effusion, pneumothorax, pneumonia,
80 support devices, or no acute abnormality).
815. If no significant abnormality is present, clearly state this.
826. Follow the provided style attributes exactly, applying them independently
83 to the Findings and Impression sections:
84 - Structure type controls the organizational pattern of the text.
85 - Temporal comparison controls whether and how prior studies are referenced.
86 - Sentence count controls the amount of text (small / medium / large).
87
88Output format:
89Return only a single JSON object with the following fields:
90
91{{
92 "findings": "<free-text radiology findings>",
93 "impression": "<free-text radiology impression>"
94}}
95
96Style attributes:
97- findings_structure_type: {findings_structure_type}
98- findings_temporal_comparison: {findings_temporal_comparison}
99- findings_sentence_count: {findings_sentence_count}
100- impression_structure_type: {impression_structure_type}
101- impression_temporal_comparison: {impression_temporal_comparison}
102- impression_sentence_count: {impression_sentence_count}"""
103
104content = []
105
106# Current frontal image (always required)
107current_frontal_image = Image.open(current_frontal_image_path)
108content.append({
109 "type": "images",
110 "image": current_frontal_image,
111})
112
113# Current lateral image (optional)
114if current_lateral_image_path:
115 current_lateral_image = Image.open(current_lateral_image_path)
116 content.append({
117 "type": "images",
118 "image": current_lateral_image,
119 })
120
121# Prior frontal image (optional)
122if prior_frontal_image_path:
123 prior_frontal_image = Image.open(prior_frontal_image_path)
124 content.append({
125 "type": "images",
126 "image": prior_frontal_image,
127 })
128
129# Instruction
130content.append({
131 "type": "text",
132 "text": instruction,
133})
134
135messages = [
136 {
137 "role": "user",
138 "content": content,
139 }
140]
141
142inputs = processor.apply_chat_template(
143 messages,
144 tokenize=True,
145 add_generation_prompt=True,
146 return_tensors="pt",
147 return_dict=True,
148)
149
150inputs = {k: v.to(model.device) for k, v in inputs.items()}
151
152with torch.no_grad():
153 generated_ids = model.generate(
154 **inputs,
155 max_new_tokens=512
156 )
157
158prompt_len = inputs["input_ids"].shape[-1]
159generated_ids_trimmed = generated_ids[:, prompt_len:]
160
161response = processor.batch_decode(
162 generated_ids_trimmed,
163 skip_special_tokens=True,
164 clean_up_tokenization_spaces=False,
165)[0]
166
167#result
168print(response)@misc{mvl-rrg-1.0,
title = {mvl-rrg-1.0},
url = {https://huggingface.co/snuh/mvl-rrg-1.0},
author = {Healthcare AI Research Institute(HARI) and Medical Vison Lab (MVL) of Seoul National University Hospital(SNUH)},
month = {January},
year = {2026}
}