public static WriteableBitmap BuildColorBitmap(CTSliceInfo ct, byte[,] normalizedPixelBuffer) { byte[] imageDataArray = new byte[ct.RowCount * ct.ColumnCount * 4]; int i = 0; for (int r = 0; r != ct.RowCount; ++r) { for (int c = 0; c != ct.ColumnCount; ++c) { byte aGrayValue = normalizedPixelBuffer[r, c]; // Black/White image: all RGB values are set to same value // Alpha value is set to 255 imageDataArray[i * 4] = aGrayValue; imageDataArray[i * 4 + 1] = aGrayValue; imageDataArray[i * 4 + 2] = aGrayValue; imageDataArray[i * 4 + 3] = 255; ++i; } } // Allocate the Bitmap WriteableBitmap bitmap = new WriteableBitmap(ct.ColumnCount, ct.RowCount, 96, 96, PixelFormats.Pbgra32, null); // Write the Pixels Int32Rect imageRectangle = new Int32Rect(0, 0, ct.ColumnCount, ct.RowCount); int imageStride = ct.ColumnCount * bitmap.Format.BitsPerPixel / 8; bitmap.WritePixels(imageRectangle, imageDataArray, imageStride, 0); return(bitmap); }
public static WriteableBitmap BuildGrey8Bitmap(CTSliceInfo ct, byte[,] normalizedPixelBuffer) { byte[] imageDataArray = new byte[ct.RowCount * ct.ColumnCount * 1]; int i = 0; for (int r = 0; r != ct.RowCount; ++r) { for (int c = 0; c != ct.ColumnCount; ++c) { byte grayValue = normalizedPixelBuffer[r, c]; imageDataArray[i] = grayValue; ++i; } } // Allocate the Bitmap WriteableBitmap bitmap = new WriteableBitmap(ct.ColumnCount, ct.RowCount, 96, 96, PixelFormats.Gray8, null); // Write the Pixels Int32Rect imageRectangle = new Int32Rect(0, 0, ct.ColumnCount, ct.RowCount); int imageStride = ct.ColumnCount * bitmap.Format.BitsPerPixel / 8; bitmap.WritePixels(imageRectangle, imageDataArray, imageStride, 0); return(bitmap); }
// build bitmap directly from Hounsfield map, shifted by 1024 public static WriteableBitmap BuildGrey16Bitmap(CTSliceInfo ct, byte[,] normalizedPixelBuffer) { byte[] imageDataArray = new byte[ct.RowCount * ct.ColumnCount * 2]; int i = 0; for (int r = 0; r != ct.RowCount; ++r) { for (int c = 0; c != ct.ColumnCount; ++c) { ushort grayValue = (ushort)(ct[r, c] + 1024); imageDataArray[i * 2 + 0] = (byte)((grayValue >> 8) & 0x00FF); imageDataArray[i * 2 + 1] = (byte)(grayValue & 0x00FF); ++i; } } // Allocate the Bitmap WriteableBitmap bitmap = new WriteableBitmap(ct.ColumnCount, ct.RowCount, 96, 96, PixelFormats.Gray16, null); // Write the Pixels Int32Rect imageRectangle = new Int32Rect(0, 0, ct.ColumnCount, ct.RowCount); int imageStride = ct.ColumnCount * bitmap.Format.BitsPerPixel / 8; bitmap.WritePixels(imageRectangle, imageDataArray, imageStride, 0); return(bitmap); }
public static byte[,] BuildNormalizedPixelBuffer(CTSliceInfo ct, int windowCenter, int windowWidth, int windowLeftBorder) { Debug.Assert(windowWidth > 0); byte[,] normalizedPixelBuffer = new byte[ct.RowCount, ct.ColumnCount]; // Normalize the Pixel value to [0,255] for (int r = 0; r != ct.RowCount; ++r) { for (int c = 0; c != ct.ColumnCount; ++c) { short aPixelValue = ct[r, c]; int aPixelValueNormalized = (255 * (aPixelValue - windowLeftBorder)) / windowWidth; if (aPixelValueNormalized <= 0) { normalizedPixelBuffer[r, c] = 0; } else if (aPixelValueNormalized >= 255) { normalizedPixelBuffer[r, c] = 255; } else { normalizedPixelBuffer[r, c] = Convert.ToByte(aPixelValueNormalized); } } } return(normalizedPixelBuffer); }
// true if value is replaced public bool Add(CTSliceInfo ct) { string fname = ct.FileName; int sliceloc = ct.SliceLoc; bool rc = _slices_by_locn.ContainsKey(sliceloc); _slices_by_name[fname] = ct; _slices_by_locn[sliceloc] = ct; return(true); }
public CTSliceInfo Clone() { CTSliceInfo ct = new CTSliceInfo(this); if (_HounsfieldPixelBuffer != null) { // make deep copy ct._HounsfieldPixelBuffer = new short[_RowCount, _ColumnCount]; Buffer.BlockCopy(_HounsfieldPixelBuffer, 0, ct._HounsfieldPixelBuffer, 0, _HounsfieldPixelBuffer.Length); } return(ct); }
// Helper method, which returns the pixel data of a CT slice as gray-scale bitmap. public static WriteableBitmap GetPixelBufferAsBitmap(CTSliceInfo ct) { int windowLeftBorder = ct.WindowCenter - (ct.WindowWidth / 2); /* * GaussBlur gb = new GaussBlur(1.0f, (float)ct.PixelSpacing_X, 5); * * short[,] bm = Apply(ct, gb); * * ct.HounsfieldPixelBuffer = bm; */ byte[,] normalizedPixelBuffer = BuildNormalizedPixelBuffer(ct, ct.WindowCenter, ct.WindowWidth, windowLeftBorder); WriteableBitmap bitmap = BuildGrey16Bitmap(ct, normalizedPixelBuffer); return(bitmap); }
// build slice list for a given patient private void ProcessAllCTs(string aPatientName, IODRepository mIODRepository) { foreach (string SOPClass in mIODRepository.GetSOPClassNames(aPatientName)) { foreach (string Study in mIODRepository.GetStudies(aPatientName, SOPClass)) { foreach (string Series in mIODRepository.GetSeries(aPatientName, SOPClass, Study)) { foreach (IOD IOD in mIODRepository.GetIODs(aPatientName, SOPClass, Study, Series)) { if (IOD.IsPixelDataProcessable()) { CTSliceInfo aCTSliceInfo = new Helper.CTSliceInfo(IOD.XDocument, IOD.FileName); _scol.Add(aCTSliceInfo); } } } } } }
public static short[,] Apply(CTSliceInfo ct, GaussBlur gb) { short[,] hmap = ct.HounsfieldPixelBuffer; int nr = ct.RowCount; int nc = ct.ColumnCount; int br = gb.br; int bc = gb.bc; float[,] blur = gb.blur; short[,] bm = new short[nr, nc]; int l = bm.Length; Array.Clear(bm, 0, bm.Length); for (int r = br; r != nr - br; ++r) { for (int c = bc; c != nc - bc; ++c) { float s = 0.0f; for (int ir = -br; ir <= br; ++ir) { int sr = r + ir; for (int ic = -bc; ic <= bc; ++ic) { int sc = c + ic; s += (float)hmap[sr, sc] * blur[ir + br, ic + bc]; } } bm[r, c] = (short)(s + 0.5f); } } return(bm); }
public CTSliceInfo(CTSliceInfo ct) { _XDocument = ct._XDocument; _FileName = ct._FileName; _ColumnCount = ct._ColumnCount; _RowCount = ct._RowCount; _SliceLoc = ct._SliceLoc; _UpperLeft_X = ct._UpperLeft_X; _UpperLeft_Y = ct._UpperLeft_Y; _UpperLeft_Z = ct._UpperLeft_Z; _PixelSpacing_X = ct._PixelSpacing_X; _PixelSpacing_Y = ct._PixelSpacing_Y; _windowCenter = ct._windowCenter; _windowWidth = ct._windowWidth; // no deep copy _HounsfieldPixelBuffer = ct._HounsfieldPixelBuffer; }
// Helper method to handle the selection change event of the IOD Tree. // a) In case the selected tree node represents only group information (Patient, SOPClass, Study, Series), the detailed view is cleared. // b) In case the selected tree node represents an IOD, the DICOM Metainformation is displayed in the DICOM Tag Tree. // c) In case the selected tree node represents a CT Slice, in addition to the DICOM Metainformation, // the ImageFlow button, the volume buttons and the bitmap is shown. private void mIODTree_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs <object> e) { TreeViewItem aSelectedNode = _IODTree.SelectedItem as TreeViewItem; if (aSelectedNode == null) { return; } // Clear old content _DICOMTagTree.Items.Clear(); _Grid.RowDefinitions.First().Height = new GridLength(0); _Grid.RowDefinitions.Last().Height = new GridLength(0); IOD anIOD = aSelectedNode.Tag as IOD; if (anIOD == null) { return; } // Set the FileName as root node string aFileName = Path.GetFileName(anIOD.FileName); TreeViewItem rootNode = new TreeViewItem() { Header = string.Format("File: {0}", aFileName) }; _DICOMTagTree.Items.Add(rootNode); // Expand the root node rootNode.IsExpanded = true; // Add all DICOM attributes to the tree foreach (XElement xe in anIOD.XDocument.Descendants("DataSet").First().Elements("DataElement")) { AddDICOMAttributeToTree(rootNode, xe); } // In case the IOD does have a processable pixel data, the ImageFlow button, the volume buttons and the bitmap is shown. // Otherwise, only the DICOM attributes are shown and the first and last grid row is hided. if (anIOD.IsPixelDataProcessable()) { CTSliceInfo ct = _scol.Retrieve(anIOD.FileName); if (ct == null) { ct = new Helper.CTSliceInfo(anIOD.XDocument, anIOD.FileName); _scol.Add(ct); } _Grid.RowDefinitions.First().Height = new GridLength(30); _Grid.RowDefinitions.Last().Height = new GridLength(ct.RowCount + 16); _Image.Source = CTSliceHelpers.GetPixelBufferAsBitmap(ct); _curCT = ct; } else { _Grid.RowDefinitions.First().Height = new GridLength(0); _Grid.RowDefinitions.Last().Height = new GridLength(0); _curCT = null; } }