NeoScan_Physician/app/assets/dicom/test-dicom-viewer.html
2025-08-20 20:42:33 +05:30

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>