github
29 Topicsdotnet maui blazor hybrid for Local viewer.
Hi Everyone, `Preformatted text` I am a dotnet Developer. Currently working with .net maui blazor hybrid app. I want to create a Local Dicom Viewer using this template for Windows. i have created a simple project as selecting a single dicom file from local drive and showing its metadata and image. Razor Page:: PAGE "/viewer" @inject IJSRuntime JSRuntime <div class="container"> <h1>DICOM Viewer</h1> <div class="row mb-3"> <div class="col"> <div class="input-group"> <InputFile OnChange="@LoadFiles" class="form-control" accept=".dcm" id="selectFile" /> <button @onclick="ResetViewer" class="btn btn-secondary">Reset</button> </div> <div class="mt-2"> <small class="text-muted">Select a DICOM file or drag and drop (if supported)</small> </div> </div> </div> <div class="row"> <div class="col-md-8"> <div id="cornerstone-element" style="width: 100%; height: 500px; border: 1px solid #ccc; position: relative;"> <div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); color: #888;"> Select a DICOM file to view </div> </div> </div> <div class="col-md-4"> <div class="card"> <div class="card-header">DICOM Info</div> <div class="card-body"> <table class="table table-sm"> <tbody> <tr><td>Transfer Syntax:</td><td id="transfersyntax">-</td></tr> <tr><td>SOP Class UID:</td><td id="sopclassuid">-</td></tr> <tr><td>SOP Instance UID:</td><td id="sopinstanceuid">-</td></tr> <tr><td>Rows:</td><td id="rows">-</td></tr> <tr><td>Columns:</td><td id="columns">-</td></tr> <tr><td>Spacing:</td><td id="spacing">-</td></tr> <tr><td>Direction:</td><td id="direction">-</td></tr> <tr><td>Origin:</td><td id="origin">-</td></tr> <tr><td>Modality:</td><td id="modality">-</td></tr> <tr><td>Pixel Representation:</td><td id="pixelrepresentation">-</td></tr> <tr><td>Bits Allocated:</td><td id="bitsallocated">-</td></tr> <tr><td>Bits Stored:</td><td id="bitsstored">-</td></tr> <tr><td>High Bit:</td><td id="highbit">-</td></tr> <tr><td>Photometric Interpretation:</td><td id="photometricinterpretation">-</td></tr> <tr><td>Window Center:</td><td id="windowcenter">-</td></tr> <tr><td>Window Width:</td><td id="windowwidth">-</td></tr> </tbody> </table> </div> </div> </div> </div> </div> @code { protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) { try { // Initialize the DICOM viewer using the global interop object await JSRuntime.InvokeVoidAsync("dicomViewerInterop.initDicomViewer"); } catch (Exception ex) { Console.WriteLine($"Error initializing DICOM viewer: {ex.Message}"); } } } private async Task LoadFiles(InputFileChangeEventArgs e) { var file = e.File; if (file != null) { try { // Read the file content using var stream = file.OpenReadStream(maxAllowedSize: 30000000); // Adjust max allowed size if needed using var ms = new MemoryStream(); await stream.CopyToAsync(ms); byte[] fileData = ms.ToArray(); var filename=file.Name; // Send the file data to JS await JSRuntime.InvokeVoidAsync("dicomViewerInterop.loadDicomFileFromArray", fileData, file.Name); } catch (Exception ex) { Console.WriteLine($"Error loading DICOM file: {ex.Message}"); } } } private async Task ResetViewer() { try { await JSRuntime.InvokeVoidAsync("dicomViewerInterop.resetViewer"); } catch (Exception ex) { Console.WriteLine($"Error resetting viewer: {ex.Message}"); } } } Javascript Interop: // Cornerstone interop code for MAUI Blazor // Global variables to store references var cornerstone, cornerstoneTools, dicomImageLoader, dicomParser; var renderingEngine = null; var viewport = null; var toolGroup = null; var initialized = false; var uids = {}; // Initialize the UIds for SOP Class descriptions function initUids() { // Common DICOM UIDs uids = { '1.2.840.10008.5.1.4.1.1.1': 'CR Image Storage', '1.2.840.10008.5.1.4.1.1.1.1': 'Digital X-Ray Image - For Presentation', '1.2.840.10008.5.1.4.1.1.1.1.1': 'Digital X-Ray Image - For Processing', '1.2.840.10008.5.1.4.1.1.2': 'CT Image Storage', '1.2.840.10008.5.1.4.1.1.3.1': 'Ultrasound Multi-frame Image Storage', '1.2.840.10008.5.1.4.1.1.4': 'MR Image Storage', '1.2.840.10008.5.1.4.1.1.6.1': 'Ultrasound Image Storage', '1.2.840.10008.5.1.4.1.1.7': 'Secondary Capture Image Storage', '1.2.840.10008.5.1.4.1.1.128': 'Positron Emission Tomography Image Storage', '1.2.840.10008.5.1.4.1.1.13.1.1': 'X-Ray 3D Angiographic Image Storage', '1.2.840.10008.5.1.4.1.1.13.1.2': 'X-Ray 3D Craniofacial Image Storage', '1.2.840.10008.5.1.4.1.1.13.1.3': 'Breast Tomosynthesis Image Storage' }; } // Check if libraries are loaded function checkLibraryLoading() { console.log("DOM content loaded"); if (typeof dicomViewer === 'undefined') { console.error("dicomViewer bundle is not loaded"); return false; } if (typeof dicomViewer.cornerstoneCore === 'undefined') { console.error(" Cornerstone3D is not loaded"); return false; } if (typeof dicomViewer.cornerstoneTools === 'undefined') { console.error(" CornerstoneTools is not loaded"); return false; } if (typeof dicomViewer.dicomImageLoader === 'undefined') { console.error(" DICOM Image Loader is not loaded"); return false; } if (typeof dicomViewer.dicomParser === 'undefined') { console.error(" DICOM Parser is not loaded"); return false; } return true; } // Initialize volume loader (compatible with Cornerstone3D v3.8.0) async function initVolumeLoader() { try { // Register the volume loader using newer API approach if (cornerstone.volumeLoader) { // Some versions might have this interface cornerstone.volumeLoader.registerUnknownVolumeLoader(); cornerstone.volumeLoader.registerVolumeLoader('dicom', function () { return { loadVolume: function () { return Promise.resolve(null); } }; }); } else { // Fallback for other versions console.log("Using fallback volume loader approach"); } } catch (error) { console.error("Error in initVolumeLoader:", error); } } // Initialize providers async function initProviders() { try { // Simplified initialization for adapting to different versions if (cornerstone.registerVolumeLoader) { cornerstone.registerVolumeLoader('dicomweb', function () { return { loadVolume: function () { return Promise.resolve(null); } }; }); } } catch (error) { console.error("Error in initProviders:", error); } } // Setup HTML function setupHTML() { const element = document.getElementById('cornerstone-element'); if (element) { // Clear any existing content element.innerHTML = ''; // Set up drop zone functionality element.addEventListener('dragover', handleDragOver, false); element.addEventListener('drop', handleFileSelect, false); } return element; } // Drag over handler function handleDragOver(evt) { evt.stopPropagation(); evt.preventDefault(); evt.dataTransfer.dropEffect = 'copy'; // Show as a copy operation } // File drop handler function handleFileSelect(evt) { evt.stopPropagation(); evt.preventDefault(); // Get the file that was dropped const files = evt.dataTransfer.files; if (files.length > 0) { const file = files[0]; // Read the file and load it const reader = new FileReader(); reader.onload = function (e) { const arrayBuffer = e.target.result; loadDicomFileFromArrayBuffer(arrayBuffer, file.name); }; reader.readAsArrayBuffer(file); } } // Initialize the DICOM viewer (called from Blazor) function initDicomViewer() { try { // Check if libraries are loaded if (!checkLibraryLoading()) { return Promise.reject("Libraries not loaded"); } // Assign the libraries from the global bundle cornerstone = dicomViewer.cornerstoneCore; cornerstoneTools = dicomViewer.cornerstoneTools; dicomImageLoader = dicomViewer.dicomImageLoader; dicomParser = dicomViewer.dicomParser; // Init UIDs initUids(); // Initialize image loader return Promise.resolve() .then(() => initVolumeLoader()) .then(() => { // Initialize DICOM image loader if (dicomImageLoader.init) { dicomImageLoader.init(); } return initProviders(); }) .then(() => { // Set CPU rendering for compatibility if (cornerstone.setUseCPURendering) { cornerstone.setUseCPURendering(true); } // Add tools if they exist if (!cornerstoneTools.addTool) { console.warn("Tool functions not available in this version"); return; } // Extract tool classes and enums const PanTool = cornerstoneTools.PanTool; const WindowLevelTool = cornerstoneTools.WindowLevelTool; const StackScrollTool = cornerstoneTools.StackScrollTool; const ZoomTool = cornerstoneTools.ZoomTool; const ToolGroupManager = cornerstoneTools.ToolGroupManager; // Get mouse bindings from enums if available const MouseBindings = cornerstoneTools.Enums?.MouseBindings || { Primary: 1, Auxiliary: 2, Secondary: 3, Wheel: 4 }; // Add tools if (PanTool) cornerstoneTools.addTool(PanTool); if (WindowLevelTool) cornerstoneTools.addTool(WindowLevelTool); if (StackScrollTool) cornerstoneTools.addTool(StackScrollTool); if (ZoomTool) cornerstoneTools.addTool(ZoomTool); // Create tool group if ToolGroupManager exists if (ToolGroupManager && ToolGroupManager.createToolGroup) { const toolGroupId = 'myToolGroup'; toolGroup = ToolGroupManager.createToolGroup(toolGroupId); // Add tools to the group if (WindowLevelTool) toolGroup.addTool(WindowLevelTool.toolName); if (PanTool) toolGroup.addTool(PanTool.toolName); if (ZoomTool) toolGroup.addTool(ZoomTool.toolName); if (StackScrollTool) toolGroup.addTool(StackScrollTool.toolName); // Set tool bindings if (WindowLevelTool) { toolGroup.setToolActive(WindowLevelTool.toolName, { bindings: [{ mouseButton: MouseBindings.Primary, // Left Click }], }); } if (PanTool) { toolGroup.setToolActive(PanTool.toolName, { bindings: [{ mouseButton: MouseBindings.Auxiliary, // Middle Click }], }); } if (ZoomTool) { toolGroup.setToolActive(ZoomTool.toolName, { bindings: [{ mouseButton: MouseBindings.Secondary, // Right Click }], }); } if (StackScrollTool) { toolGroup.setToolActive(StackScrollTool.toolName, { bindings: [{ mouseButton: MouseBindings.Wheel }], }); } } // Setup the HTML element const element = setupHTML(); if (!element) { console.error('Element not found'); return; } // Create the rendering engine if the RenderingEngine class exists if (cornerstone.RenderingEngine) { const renderingEngineId = 'myRenderingEngine'; renderingEngine = new cornerstone.RenderingEngine(renderingEngineId); // Create the viewport const viewportId = 'CT_STACK'; const viewportInput = { viewportId, type: cornerstone.Enums.ViewportType.STACK, element, defaultOptions: { background: [0.2, 0, 0.2], }, }; // Enable the viewport renderingEngine.enableElement(viewportInput); // Get the viewport we created viewport = renderingEngine.getViewport(viewportId); // Add the viewport to the tool group if (toolGroup) { toolGroup.addViewport(viewportId, renderingEngineId); } } else { console.error("RenderingEngine not available"); return; } // Mark as initialized initialized = true; console.log('DICOM viewer initialized successfully'); return Promise.resolve(); }); } catch (error) { console.error('Error initializing DICOM viewer:', error); return Promise.reject(error); } } // Load a DICOM file from an array buffer function loadDicomFileFromArrayBuffer(arrayBuffer, fileName) { if (!initialized || !viewport) { console.error('DICOM viewer not initialized'); return Promise.reject('DICOM viewer not initialized'); } try { // Use the DICOM Image Loader to create an image ID const uint8Array = new Uint8Array(arrayBuffer); let imageId; if (dicomImageLoader.wadouri.fileManager.addByteArray) { imageId = dicomImageLoader.wadouri.fileManager.addByteArray(uint8Array); } else if (dicomImageLoader.wadouri.fileManager.add) { // Create a File-like object const blob = new Blob([uint8Array]); const file = new File([blob], fileName); console.log(file, fileName); imageId = dicomImageLoader.wadouri.fileManager.add(file); console.log(imageId); } else { console.error("No appropriate method to load DICOM file"); return Promise.reject("No appropriate method to load DICOM file"); } // Create a stack with this image const stack = [imageId]; // Set the stack on the viewport viewport.setStack(stack) // Render the image viewport.render(); // Update metadata display updateMetadataDisplay(imageId); console.log('DICOM file loaded successfully:', fileName); // return Promise.resolve(); } catch (error) { console.error('Error loading DICOM file:', error); return Promise.reject(error); } } // Update the metadata display function updateMetadataDisplay(imageId) { try { if (!viewport) return; const imageData = viewport.getImageData(); const { metaData } = cornerstone; // Get metadata from Cornerstone const pixelModule = metaData.get('imagePixelModule', imageId); const voiLutModule = metaData.get('voiLutModule', imageId); const sopCommonModule = metaData.get('sopCommonModule', imageId); const transferSyntax = metaData.get('transferSyntax', imageId); // Update UI elements with the metadata document.getElementById('transfersyntax').innerHTML = transferSyntax?.transferSyntaxUID || '-'; if (sopCommonModule?.sopClassUID) { const sopClassDesc = uids[sopCommonModule.sopClassUID] || 'Unknown'; document.getElementById('sopclassuid').innerHTML = `${sopCommonModule.sopClassUID} [${sopClassDesc}]`; } else { document.getElementById('sopclassuid').innerHTML = '-'; } document.getElementById('sopinstanceuid').innerHTML = sopCommonModule?.sopInstanceUID || '-'; document.getElementById('rows').innerHTML = imageData?.dimensions[0] || '-'; document.getElementById('columns').innerHTML = imageData?.dimensions[1] || '-'; document.getElementById('spacing').innerHTML = imageData?.spacing.join('\\') || '-'; const formattedDirection = imageData?.direction ? imageData.direction.map(x => Math.round(x * 100) / 100).join(',') : '-'; document.getElementById('direction').innerHTML = formattedDirection; const formattedOrigin = imageData?.origin ? imageData.origin.map(x => Math.round(x * 100) / 100).join(',') : '-'; document.getElementById('origin').innerHTML = formattedOrigin; document.getElementById('modality').innerHTML = imageData?.metadata?.Modality || '-'; document.getElementById('pixelrepresentation').innerHTML = pixelModule?.pixelRepresentation || '-'; document.getElementById('bitsallocated').innerHTML = pixelModule?.bitsAllocated || '-'; document.getElementById('bitsstored').innerHTML = pixelModule?.bitsStored || '-'; document.getElementById('highbit').innerHTML = pixelModule?.highBit || '-'; document.getElementById('photometricinterpretation').innerHTML = pixelModule?.photometricInterpretation || '-'; document.getElementById('windowcenter').innerHTML = voiLutModule?.windowCenter || '-'; document.getElementById('windowwidth').innerHTML = voiLutModule?.windowWidth || '-'; } catch (error) { console.error('Error updating metadata display:', error); } } // Load a DICOM file from a byte array (called from Blazor) function loadDicomFileFromArray(byteArray, fileName) { const arrayBuffer = new Uint8Array(byteArray).buffer; return loadDicomFileFromArrayBuffer(arrayBuffer, fileName); } // Reset the viewer function resetViewer() { if (viewport) { viewport.reset(); viewport.render(); // Reset the metadata display const elements = [ 'transfersyntax', 'sopclassuid', 'sopinstanceuid', 'rows', 'columns', 'spacing', 'direction', 'origin', 'modality', 'pixelrepresentation', 'bitsallocated', 'bitsstored', 'highbit', 'photometricinterpretation', 'windowcenter', 'windowwidth' ]; elements.forEach(id => { document.getElementById(id).innerHTML = '-'; }); return Promise.resolve(); } return Promise.reject("Viewport not initialized"); } // Add to window for accessibility from Blazor window.dicomViewerInterop = { initDicomViewer: initDicomViewer, loadDicomFileFromArray: loadDicomFileFromArray, resetViewer: resetViewer }; // Log when the script is loaded console.log("Cornerstone interop script loaded"); // Check if DOM is already loaded, otherwise wait for it if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', checkLibraryLoading); } else { checkLibraryLoading(); } i am able to load the cornerstone modules from my bundle file , but unable to get metadata and unable to render the image.25Views0likes0CommentsPipeline Extraction Step Fails: Archive File Not Found
Issue Summary The archive file is not found during the extraction step in the pipeline, causing the task to fail. Steps to Reproduce Run the pipeline with the ExtractFiles task configured to extract a previously archived file. The task attempts to locate and extract the file from the expected directory. The extraction step fails due to a missing or inaccessible archive file. Expected Results The extraction step should successfully locate the archive file and extract its contents. Actual Results The pipeline fails at the extraction step. The archive file is not found at the expected location. Error Details Relative file path: _$(Build.BuildDefinition)/80-build/80.zip resolving to: /home/vsts/work/r1/a/_$(Build.BuildDefinition)/80-build/80.zip Error Message: Error: Specified archive: /home/vsts/work/r1/a/_$(Build.BuildDefinition)/80-build/80.zip cannot be extracted because it cannot be accessed. Error: ENOENT: no such file or directory, stat '/home/vsts/work/r1/a/_$(Build.BuildDefinition)/80-build/80.zip'31Views0likes0CommentsPlease suggest “A Basic Understanding of Python” via Microsoft Learn
I’ve seen many posts and discussions about beginner Python courses on Microsoft Learn, such as “A Basic Understanding of Python” or " take your first steps with Python" (https://learn.microsoft.com/en-us/training/browse/?WT.mc_id=academic-77958-bethanycheum). Could you please suggest a working link to study basic Python on Microsoft Learn? All the links I found in previous posts are not working.100Views0likes1CommentSeeking Preferred Visitors for Microsoft Student Ambassador Program!
Hello everyone! I hope this message finds you well! My name is Simon, and I’m a Microsoft Student Ambassador at Aalborg University, Denmark. I’m excited to connect with fellow students and tech enthusiasts here! As part of my role, I’m aiming to gather 250 preferred visitors who are interested in learning more about Microsoft technologies and the opportunities available through the Student Ambassador program. Why Join? Learn: Gain insights into Microsoft tools and resources. Network: Connect with like-minded peers and professionals. Participate: Engage in exciting events and workshops. If you’re interested or know someone who might be, please feel free to reach out or share this post! You can register as a preferred visitor here: https://Events.Microsoft.Com/?wt.mc_id=studentamb_433185 Thank you, and I look forward to connecting with you all! Best, Simon140Views2likes0CommentsThere is an error in the GitHub Foundations course
I started going through the GitHub Foundations Microsoft Learn course a few weeks ago. I got to Module 7, "Communicate effectively on GitHub using Markdown". I was in Unit 3, "Communicate using Markdown", when the instructions told me to go to a tab on the page named, "File changed". There is no tab on that page named "File changed". It's been a week trying to find out what the answer is to my problem. Please, help me to continue doing this course!!!Solved453Views0likes4CommentsCaller line number routing
Hi Using MS teams with Operator connect, is it possible to setup CLI routing as you can on some PBX solutions. IE, caller ringing in has a number xxx555 I want this caller with this cli prestented to be able to reach a certain resource account (queue or AA) and ignore or redirect all other callers to another resource account.263Views0likes0Comments