444 lines
14 KiB
HTML
444 lines
14 KiB
HTML
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>DICOM Viewer Test</title>
|
|
<style>
|
|
body {
|
|
margin: 0;
|
|
padding: 20px;
|
|
background: #f5f5f5;
|
|
font-family: Arial, sans-serif;
|
|
}
|
|
.container {
|
|
max-width: 800px;
|
|
margin: 0 auto;
|
|
background: white;
|
|
padding: 20px;
|
|
border-radius: 8px;
|
|
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
|
}
|
|
.test-section {
|
|
margin-bottom: 20px;
|
|
padding: 15px;
|
|
border: 1px solid #ddd;
|
|
border-radius: 5px;
|
|
}
|
|
.url-input {
|
|
width: 100%;
|
|
padding: 10px;
|
|
border: 1px solid #ccc;
|
|
border-radius: 4px;
|
|
margin-bottom: 10px;
|
|
}
|
|
.load-button {
|
|
background: #2196F3;
|
|
color: white;
|
|
border: none;
|
|
padding: 10px 20px;
|
|
border-radius: 4px;
|
|
cursor: pointer;
|
|
}
|
|
.load-button:hover {
|
|
background: #1976D2;
|
|
}
|
|
.viewer-container {
|
|
margin-top: 20px;
|
|
border: 2px solid #ddd;
|
|
border-radius: 8px;
|
|
overflow: hidden;
|
|
min-height: 400px;
|
|
}
|
|
.status {
|
|
padding: 10px;
|
|
background: #f0f0f0;
|
|
border-bottom: 1px solid #ddd;
|
|
font-weight: bold;
|
|
}
|
|
#dicomImage {
|
|
width: 100%;
|
|
height: 400px;
|
|
background: #000;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
color: white;
|
|
}
|
|
.error {
|
|
color: #F44336;
|
|
background: #FFEBEE;
|
|
padding: 10px;
|
|
border-radius: 4px;
|
|
margin: 10px 0;
|
|
}
|
|
.success {
|
|
color: #4CAF50;
|
|
background: #E8F5E8;
|
|
padding: 10px;
|
|
border-radius: 4px;
|
|
margin: 10px 0;
|
|
}
|
|
.warning {
|
|
color: #FF9800;
|
|
background: #FFF3E0;
|
|
padding: 10px;
|
|
border-radius: 4px;
|
|
margin: 10px 0;
|
|
}
|
|
.sample-urls {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
|
gap: 10px;
|
|
margin-bottom: 20px;
|
|
}
|
|
.sample-url {
|
|
background: #E3F2FD;
|
|
padding: 10px;
|
|
border-radius: 4px;
|
|
cursor: pointer;
|
|
border: 1px solid #BBDEFB;
|
|
}
|
|
.sample-url:hover {
|
|
background: #BBDEFB;
|
|
}
|
|
.dicom-info {
|
|
background: #F5F5F5;
|
|
padding: 15px;
|
|
border-radius: 4px;
|
|
margin: 15px 0;
|
|
font-family: monospace;
|
|
font-size: 12px;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<h1>DICOM Viewer Test</h1>
|
|
<p>Test the DICOM viewer functionality in your browser before using it in React Native.</p>
|
|
|
|
<div class="test-section">
|
|
<h3>Sample DICOM URLs</h3>
|
|
<div class="sample-urls">
|
|
<div class="sample-url" onclick="loadSampleUrl('https://ohif-dicom-json-example.s3.amazonaws.com/LIDC-IDRI-0001/01-01-2000-30178/3000566.000000-03192/1-001.dcm')">
|
|
<strong>Sample 1:</strong><br>
|
|
LIDC-IDRI-0001
|
|
</div>
|
|
<div class="sample-url" onclick="loadSampleUrl('https://ohif-dicom-json-example.s3.amazonaws.com/LIDC-IDRI-0001/01-01-2000-30178/3000566.000000-03192/1-002.dcm')">
|
|
<strong>Sample 2:</strong><br>
|
|
LIDC-IDRI-0001
|
|
</div>
|
|
<div class="sample-url" onclick="loadSampleUrl('https://ohif-dicom-json-example.s3.amazonaws.com/LIDC-IDRI-0001/01-01-2000-30178/3000566.000000-03192/1-003.dcm')">
|
|
<strong>Sample 3:</strong><br>
|
|
LIDC-IDRI-0001
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="test-section">
|
|
<h3>Custom DICOM URL</h3>
|
|
<input type="text" id="customUrl" class="url-input" placeholder="Enter DICOM URL here..." />
|
|
<button onclick="loadCustomUrl()" class="load-button">Load DICOM Image</button>
|
|
</div>
|
|
|
|
<div class="viewer-container">
|
|
<div class="status" id="status">Ready to load DICOM image</div>
|
|
<div id="dicomImage">
|
|
<div>Click a sample URL above or enter a custom URL to load a DICOM image</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="dicomInfo" class="dicom-info" style="display: none;">
|
|
<strong>DICOM Information:</strong><br>
|
|
<div id="dicomInfoContent"></div>
|
|
</div>
|
|
|
|
<div id="messages"></div>
|
|
</div>
|
|
|
|
<script>
|
|
let cornerstone = null;
|
|
let cornerstoneWADOImageLoader = null;
|
|
let dicomParser = null;
|
|
let isLoaded = false;
|
|
|
|
// Load sample URL
|
|
function loadSampleUrl(url) {
|
|
document.getElementById('customUrl').value = url;
|
|
loadDicomImage(url);
|
|
}
|
|
|
|
// Load custom URL
|
|
function loadCustomUrl() {
|
|
const url = document.getElementById('customUrl').value.trim();
|
|
if (url) {
|
|
loadDicomImage(url);
|
|
} else {
|
|
showMessage('Please enter a valid URL', 'error');
|
|
}
|
|
}
|
|
|
|
// Show message
|
|
function showMessage(message, type = 'info') {
|
|
const messagesDiv = document.getElementById('messages');
|
|
const messageDiv = document.createElement('div');
|
|
messageDiv.className = type;
|
|
messageDiv.textContent = message;
|
|
messagesDiv.appendChild(messageDiv);
|
|
|
|
// Remove message after 5 seconds
|
|
setTimeout(() => {
|
|
messageDiv.remove();
|
|
}, 5000);
|
|
}
|
|
|
|
// Update status
|
|
function updateStatus(message) {
|
|
document.getElementById('status').textContent = message;
|
|
}
|
|
|
|
// Show DICOM info
|
|
function showDicomInfo(info) {
|
|
const infoDiv = document.getElementById('dicomInfo');
|
|
const contentDiv = document.getElementById('dicomInfoContent');
|
|
|
|
if (info) {
|
|
contentDiv.innerHTML = info;
|
|
infoDiv.style.display = 'block';
|
|
} else {
|
|
infoDiv.style.display = 'none';
|
|
}
|
|
}
|
|
|
|
// Load libraries
|
|
async function loadLibraries() {
|
|
try {
|
|
updateStatus('Loading DICOM viewer libraries...');
|
|
|
|
// Load DICOM Parser first
|
|
await loadScript('https://unpkg.com/dicom-parser@1.8.6/dist/dicomParser.min.js');
|
|
dicomParser = window.dicomParser;
|
|
showMessage('DICOM Parser loaded successfully', 'success');
|
|
|
|
// Load Cornerstone Core
|
|
await loadScript('https://unpkg.com/cornerstone-core@2.3.0/dist/cornerstone.js');
|
|
cornerstone = window.cornerstone;
|
|
|
|
// Load Cornerstone WADO Image Loader with fallback
|
|
await loadCornerstoneWADO();
|
|
|
|
isLoaded = true;
|
|
updateStatus('Libraries loaded successfully');
|
|
showMessage('All DICOM viewer libraries loaded successfully', 'success');
|
|
|
|
// Initialize viewer
|
|
const element = document.getElementById('dicomImage');
|
|
cornerstone.enable(element);
|
|
|
|
} catch (error) {
|
|
updateStatus('Failed to load libraries');
|
|
showMessage(`Failed to load libraries: ${error.message}`, 'error');
|
|
}
|
|
}
|
|
|
|
// Load script
|
|
function loadScript(src) {
|
|
return new Promise((resolve, reject) => {
|
|
const script = document.createElement('script');
|
|
script.src = src;
|
|
script.onload = resolve;
|
|
script.onerror = () => reject(new Error(`Failed to load script: ${src}`));
|
|
document.head.appendChild(script);
|
|
});
|
|
}
|
|
|
|
// Load Cornerstone WADO Image Loader with fallback
|
|
function loadCornerstoneWADO() {
|
|
return new Promise((resolve, reject) => {
|
|
updateStatus('Loading Cornerstone WADO Image Loader...');
|
|
|
|
// Try multiple sources for WADO loader
|
|
const wadoSources = [
|
|
'https://unpkg.com/cornerstone-wado-image-loader@4.17.1/dist/cornerstoneWADOImageLoader.js',
|
|
'https://unpkg.com/cornerstone-wado-image-loader@4.16.0/dist/cornerstoneWADOImageLoader.js',
|
|
'https://unpkg.com/cornerstone-wado-image-loader@4.15.0/dist/cornerstoneWADOImageLoader.js',
|
|
'https://cdn.jsdelivr.net/npm/cornerstone-wado-image-loader@4.17.1/dist/cornerstoneWADOImageLoader.js'
|
|
];
|
|
|
|
let currentSourceIndex = 0;
|
|
|
|
function tryNextSource() {
|
|
if (currentSourceIndex >= wadoSources.length) {
|
|
reject(new Error('All WADO Image Loader sources failed'));
|
|
return;
|
|
}
|
|
|
|
const currentSource = wadoSources[currentSourceIndex];
|
|
updateStatus(`Trying WADO source ${currentSourceIndex + 1}: ${currentSource.split('/').pop()}`);
|
|
|
|
const wadoScript = document.createElement('script');
|
|
wadoScript.src = currentSource;
|
|
|
|
wadoScript.onload = () => {
|
|
try {
|
|
cornerstoneWADOImageLoader = window.cornerstoneWADOImageLoader;
|
|
if (cornerstoneWADOImageLoader && cornerstone) {
|
|
cornerstoneWADOImageLoader.external.cornerstone = cornerstone;
|
|
showMessage(`WADO Image Loader loaded successfully from: ${currentSource.split('/').pop()}`, 'success');
|
|
resolve();
|
|
} else {
|
|
throw new Error('WADO loader not properly initialized');
|
|
}
|
|
} catch (error) {
|
|
showMessage(`WADO loader initialization failed: ${error.message}`, 'warning');
|
|
currentSourceIndex++;
|
|
tryNextSource();
|
|
}
|
|
};
|
|
|
|
wadoScript.onerror = (error) => {
|
|
showMessage(`Failed to load WADO from ${currentSource.split('/').pop()}`, 'warning');
|
|
currentSourceIndex++;
|
|
tryNextSource();
|
|
};
|
|
|
|
// Set timeout for loading
|
|
const timeout = setTimeout(() => {
|
|
showMessage(`WADO loader timeout from ${currentSource.split('/').pop()}`, 'warning');
|
|
currentSourceIndex++;
|
|
tryNextSource();
|
|
}, 8000);
|
|
|
|
wadoScript.onload = () => {
|
|
clearTimeout(timeout);
|
|
try {
|
|
cornerstoneWADOImageLoader = window.cornerstoneWADOImageLoader;
|
|
if (cornerstoneWADOImageLoader && cornerstone) {
|
|
cornerstoneWADOImageLoader.external.cornerstone = cornerstone;
|
|
showMessage(`WADO Image Loader loaded successfully from: ${currentSource.split('/').pop()}`, 'success');
|
|
resolve();
|
|
} else {
|
|
throw new Error('WADO loader not properly initialized');
|
|
}
|
|
} catch (error) {
|
|
showMessage(`WADO loader initialization failed: ${error.message}`, 'warning');
|
|
currentSourceIndex++;
|
|
tryNextSource();
|
|
}
|
|
};
|
|
|
|
document.head.appendChild(wadoScript);
|
|
}
|
|
|
|
tryNextSource();
|
|
});
|
|
}
|
|
|
|
// Load DICOM image
|
|
async function loadDicomImage(url) {
|
|
if (!isLoaded) {
|
|
showMessage('Libraries not loaded yet, please wait...', 'error');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
updateStatus('Loading DICOM image...');
|
|
showMessage(`Loading DICOM image from: ${url}`, 'info');
|
|
|
|
// Test URL accessibility
|
|
await testUrl(url);
|
|
|
|
// Validate DICOM with parser
|
|
let dicomInfo = null;
|
|
try {
|
|
dicomInfo = await validateDicomWithParser(url);
|
|
showMessage('DICOM file validated successfully', 'success');
|
|
} catch (parserError) {
|
|
showMessage(`DICOM validation warning: ${parserError.message}`, 'warning');
|
|
}
|
|
|
|
const element = document.getElementById('dicomImage');
|
|
const image = await cornerstone.loadImage(`wadouri:${url}`);
|
|
cornerstone.displayImage(element, image);
|
|
|
|
updateStatus('DICOM image loaded successfully');
|
|
showMessage('DICOM image loaded successfully!', 'success');
|
|
|
|
// Display DICOM information if available
|
|
if (dicomInfo) {
|
|
displayDicomInfo(dicomInfo);
|
|
}
|
|
|
|
} catch (error) {
|
|
updateStatus('Failed to load DICOM image');
|
|
showMessage(`Failed to load DICOM image: ${error.message}`, 'error');
|
|
showDicomInfo(null);
|
|
}
|
|
}
|
|
|
|
// Validate DICOM with parser
|
|
async function validateDicomWithParser(url) {
|
|
try {
|
|
const response = await fetch(url);
|
|
const arrayBuffer = await response.arrayBuffer();
|
|
|
|
if (!dicomParser) {
|
|
throw new Error('DICOM Parser not available');
|
|
}
|
|
|
|
const dataSet = dicomParser.parseDicom(arrayBuffer);
|
|
return dataSet;
|
|
} catch (error) {
|
|
throw new Error(`DICOM validation failed: ${error.message}`);
|
|
}
|
|
}
|
|
|
|
// Display DICOM information
|
|
function displayDicomInfo(dataSet) {
|
|
try {
|
|
const info = [
|
|
`Patient Name: ${dataSet.string('x00100010') || 'Unknown'}`,
|
|
`Patient ID: ${dataSet.string('x00100020') || 'Unknown'}`,
|
|
`Modality: ${dataSet.string('x00080060') || 'Unknown'}`,
|
|
`Study Date: ${dataSet.string('x00080020') || 'Unknown'}`,
|
|
`Study Description: ${dataSet.string('x00081030') || 'Unknown'}`,
|
|
`Manufacturer: ${dataSet.string('x00080070') || 'Unknown'}`,
|
|
`Image Size: ${dataSet.uint16('x00280010') || 'Unknown'} x ${dataSet.uint16('x00280011') || 'Unknown'}`,
|
|
`Bits Allocated: ${dataSet.uint16('x00280100') || 'Unknown'}`,
|
|
`Samples per Pixel: ${dataSet.uint16('x00280002') || 'Unknown'}`
|
|
].join('<br>');
|
|
|
|
showDicomInfo(info);
|
|
} catch (error) {
|
|
console.error('Error displaying DICOM info:', error);
|
|
showDicomInfo('Error displaying DICOM information');
|
|
}
|
|
}
|
|
|
|
// Test URL accessibility
|
|
async function testUrl(url) {
|
|
try {
|
|
const response = await fetch(url, { method: 'HEAD' });
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
}
|
|
|
|
const contentType = response.headers.get('content-type');
|
|
console.log('Content-Type:', contentType);
|
|
|
|
if (contentType && !contentType.includes('application/dicom') && !contentType.includes('image/')) {
|
|
console.warn('Warning: Unexpected content type:', contentType);
|
|
}
|
|
|
|
} catch (error) {
|
|
throw new Error(`URL not accessible: ${error.message}`);
|
|
}
|
|
}
|
|
|
|
// Initialize when page loads
|
|
window.addEventListener('load', loadLibraries);
|
|
</script>
|
|
</body>
|
|
</html>
|