private void AddElemToNode(DICOMElement elem, TreeNode BaseNode) { string VR = elem.VR; TreeNode newNode = BaseNode.Nodes.Add(elem.Dump()); if (VR == "SQ") { List <SQItem> sqItems = (List <SQItem>)elem.Data; int iSQCnt = sqItems.Count; foreach (SQItem sqItem in sqItems) { TreeNode SQNode2 = newNode.Nodes.Add("SQ Item:"); foreach (DICOMElement nElem in sqItem.Elements) { AddElemToNode(nElem, SQNode2); } if (sqItem.IsEncapsulatedImage) { SQNode2.Nodes.Add("Encapsulated Image Data: " + sqItem.EncapsulatedImageData.Length + " bytes"); } } } }
/// <summary> /// This method writes the writes the byte data to the incoming Binary Writer /// </summary> /// <param name="b">The Binary writer to write the bytes to</param> /// <param name="d">The DicomObject containing the data to be written</param> /// <param name="isLittleEndian">A boolean that indicates whether or not the bytes are written in little or big endian.</param> public static void WriteData(BinaryWriter b, DICOMElement d, bool isLittleEndian) { if (d.IsLittleEndian != isLittleEndian) { d.Encode(isLittleEndian); } foreach (byte bt in d.ByteData) { b.Write(bt); } }
/// <summary> /// Gets or sets the <see cref="DICOMSharp.Data.Elements.DICOMElement"/> with the specified tag. This is basically a shortcut helper /// for the Elements property. /// /// Note: If you attempt to retrieve a tag this way that doesn't currently exist in the DICOMData, it will be created and added to /// the Elements list. /// </summary> public DICOMElement this[uint tag] { get { //Return existing if it's here if (Elements.ContainsKey(tag)) { return(Elements[tag]); } //Create new element, add to storage, and return DICOMElement newElem = DICOMElement.CreateFromTag(tag); Elements[tag] = newElem; return(newElem); } set { Elements[tag] = value; } }
internal bool ParseStream(Stream stream, TransferSyntax transferSyntax, bool allowSyntaxChanges, bool loadImageData, ILogger logger) { SwappableBinaryReader sr = new SwappableBinaryReader(stream); long readPosition = stream.Position; bool inGroup2 = false; if (transferSyntax.MSBSwap) //this should be applicable... { sr.ToggleSwapped(); } TransferSyntax parsedSyntax = null; while (readPosition + 8 < stream.Length) { //Read in group/element info ushort group = sr.ReadUInt16(); ushort elem = sr.ReadUInt16(); //Leaving the header? if (inGroup2 != (group == 2)) { if (transferSyntax.MSBSwap) { sr.ToggleSwapped(); } inGroup2 = (group == 2); } //Stop loading if we're at image data and not supposed to read it var skipData = !loadImageData && group == 0x7FE0 && elem == 0x0010; //Make element uint outLen; DICOMElement nelem; try { nelem = DICOMElement.Parse(group, elem, logger, transferSyntax, sr, null, skipData, out outLen); } catch (Exception e) { logger.Log(LogLevel.Error, "Exception in DICOMElement.Parse: " + e.ToString()); return(false); } //Debugging: //Console.WriteLine(nelem.Dump()); //Store reading position in case it's useful later nelem.ReadPosition = readPosition; //Store element in lookup array Elements[nelem.Tag] = nelem; //Store transfer syntax change for after the header if (nelem.Tag == DICOMTags.TransferSyntaxUID) { parsedSyntax = TransferSyntaxes.Lookup((string)nelem.Data); if (allowSyntaxChanges) { transferSyntax = parsedSyntax; } } //update read position pointer readPosition = stream.Position; } //Store whatever TS we ended up with TransferSyntax = (parsedSyntax != null) ? parsedSyntax : transferSyntax; return(true); }
private static DICOMElement CreateVRObject(string vr, Constants.EncodeType encType, byte[] data, Tag t, bool isLittleEndian, bool isIndefinite) { switch (vr) { case "CS": CodeString cs = new CodeString(); cs.ByteData = data; cs.EncodeType = encType; cs.IsLittleEndian = isLittleEndian; cs.Tag = t; return cs; case "SH": ShortString sh = new ShortString(); sh.ByteData = data; sh.EncodeType = encType; sh.IsLittleEndian = isLittleEndian; sh.Tag = t; return sh; case "LO": LongString lo = new LongString(); lo.ByteData = data; lo.EncodeType = encType; lo.IsLittleEndian = isLittleEndian; lo.Tag = t; return lo; case "ST": ShortText st = new ShortText(); st.ByteData = data; st.EncodeType = encType; st.IsLittleEndian = isLittleEndian; st.Tag = t; return st; case "LT": LongText lt = new LongText(); lt.ByteData = data; lt.EncodeType = encType; lt.IsLittleEndian = isLittleEndian; lt.Tag = t; return lt; case "UT": UnlimitedText ut = new UnlimitedText(); ut.ByteData = data; ut.EncodeType = encType; ut.IsLittleEndian = isLittleEndian; ut.Tag = t; return ut; case "AE": ApplicationEntity ae = new ApplicationEntity(); ae.ByteData = data; ae.EncodeType = encType; ae.IsLittleEndian = isLittleEndian; ae.Tag = t; return ae; case "PN": PersonsName pn = new PersonsName(); pn.ByteData = data; pn.EncodeType = encType; pn.IsLittleEndian = isLittleEndian; pn.Tag = t; return pn; case "UI": UniqueIdentifier ui = new UniqueIdentifier(); ui.ByteData = data; ui.EncodeType = encType; ui.IsLittleEndian = isLittleEndian; ui.Tag = t; return ui; case "DA": DateVR da = new DateVR(); da.ByteData = data; da.EncodeType = encType; da.IsLittleEndian = isLittleEndian; da.Tag = t; return da; case "TM": TimeVR tm = new TimeVR(); tm.ByteData = data; tm.EncodeType = encType; tm.IsLittleEndian = isLittleEndian; tm.Tag = t; return tm; case "DT": DateTimeVR dt = new DateTimeVR(); dt.ByteData = data; dt.EncodeType = encType; dt.IsLittleEndian = isLittleEndian; dt.Tag = t; return dt; case "AS": AgeString aSt = new AgeString(); aSt.ByteData = data; aSt.EncodeType = encType; aSt.IsLittleEndian = isLittleEndian; aSt.Tag = t; return aSt; case "IS": IntegerString iSt = new IntegerString(); iSt.ByteData = data; iSt.EncodeType = encType; iSt.IsLittleEndian = isLittleEndian; iSt.Tag = t; return iSt; case "DS": DecimalString ds = new DecimalString(); ds.ByteData = data; ds.EncodeType = encType; ds.IsLittleEndian = isLittleEndian; ds.Tag = t; return ds; case "SS": SignedShort ss = new SignedShort(); ss.ByteData = data; ss.EncodeType = encType; ss.IsLittleEndian = isLittleEndian; ss.Tag = t; return ss; case "US": UnsignedShort us = new UnsignedShort(); us.ByteData = data; us.EncodeType = encType; us.IsLittleEndian = isLittleEndian; us.Tag = t; return us; case "SL": SignedLong sl = new SignedLong(); sl.ByteData = data; sl.EncodeType = encType; sl.IsLittleEndian = isLittleEndian; sl.Tag = t; return sl; case "UL": UnsignedLong ul = new UnsignedLong(); ul.ByteData = data; ul.EncodeType = encType; ul.IsLittleEndian = isLittleEndian; ul.Tag = t; return ul; case "AT": AttributeTag at = new AttributeTag(); at.ByteData = data; at.EncodeType = encType; at.IsLittleEndian = isLittleEndian; at.Tag = t; return at; case "FL": FloatingPointSingle fl = new FloatingPointSingle(); fl.ByteData = data; fl.EncodeType = encType; fl.IsLittleEndian = isLittleEndian; fl.Tag = t; return fl; case "FD": FloatingPointDouble fd = new FloatingPointDouble(); fd.ByteData = data; fd.EncodeType = encType; fd.IsLittleEndian = isLittleEndian; fd.Tag = t; return fd; case "OB": if (t.Id == TagHelper.PIXEL_DATA) { PixelData fd1 = new PixelData(data, encType, isLittleEndian, "OB", isIndefinite); fd1.Format = isIndefinite ? FrameDataFormat.ENCAPSULATED : FrameDataFormat.NATIVE; fd1.EncodeType = encType; fd1.IsLittleEndian = isLittleEndian; fd1.Tag = t; return fd1; } else { OtherByteString ob = new OtherByteString(); ob.ByteData = data; ob.EncodeType = encType; ob.IsLittleEndian = isLittleEndian; ob.Tag = t; return ob; } case "OW": if (t.Id == TagHelper.PIXEL_DATA) { PixelData fd2 = new PixelData(data, encType, isLittleEndian, "OW", isIndefinite); fd2.Format = isIndefinite ? FrameDataFormat.ENCAPSULATED : FrameDataFormat.NATIVE; fd2.EncodeType = encType; fd2.IsLittleEndian = isLittleEndian; fd2.Tag = t; return fd2; } else { OtherWordString ow = new OtherWordString(); ow.ByteData = data; ow.EncodeType = encType; ow.IsLittleEndian = isLittleEndian; ow.Tag = t; return ow; } case "OF": OtherFloatString of = new OtherFloatString(); of.ByteData = data; of.EncodeType = encType; of.IsLittleEndian = isLittleEndian; of.Tag = t; return of; case "SQ": Sequence s = new Sequence(); s.ByteData = data; s.EncodeType = encType; s.IsLittleEndian = isLittleEndian; s.Tag = t; s.ReadChildren(); return s; default: //Case for unknown VR DICOMElement dOb = new DICOMElement(); dOb.ByteData = data; dOb.EncodeType = encType; dOb.IsLittleEndian = isLittleEndian; dOb.Tag = t; return dOb; } }
/// <summary> /// This method reads the next four bytes of a binary reader /// object and parses out the DICOM tag. It then sets this tag as /// the tag for the DicomObject parameter. /// </summary> /// <param name="r">The BinaryReader object that is coming from the DicomFile.</param> /// <param name="d">The DicomObject that the tag will be set to.</param> public static void ReadTag(BinaryReader r, DICOMElement d) { try { byte[] tagBytes = new byte[4]; r.Read(tagBytes, 0, 4); d.Tag = new Tag(tagBytes, d.IsLittleEndian); } catch (Exception) { Console.WriteLine("Could not read tag."); } }
internal static bool Uncompress(DICOMData data) { if (data.TransferSyntax.Compression != CompressionInfo.None) { if (!data.Elements.ContainsKey(DICOMTags.PixelData)) { return(false); } //Compressed in some way. Pull the inner data out... DICOMElement pixelDataElem = data.Elements[DICOMTags.PixelData]; ushort bitsAllocated = (ushort)data.Elements[DICOMTags.BitsAllocated].Data; //Get other image metrics ushort imWidth = (ushort)data.Elements[DICOMTags.ImageWidth].Data; ushort imHeight = (ushort)data.Elements[DICOMTags.ImageHeight].Data; bool hasNumFramesTag = data.Elements.ContainsKey(DICOMTags.NumberOfFrames); int numFrames = 1; if (hasNumFramesTag && int.TryParse((string)data.Elements[DICOMTags.NumberOfFrames].Data, out numFrames)) { } int samplesPerPixel = (data.Elements.ContainsKey(DICOMTags.SamplesPerPixel) ? (int)(ushort)data.Elements[DICOMTags.SamplesPerPixel].Data : 1); int planarConfiguration = (data.Elements.ContainsKey(DICOMTags.PlanarConfiguration) ? (int)(ushort)data.Elements[DICOMTags.PlanarConfiguration].Data : 0); string photoInterp = (string)data[DICOMTags.PhotometricInterpretation].Data; bool ybr = (photoInterp == "YBR_FULL" || photoInterp == "YBR_FULL_422"); if (pixelDataElem is DICOMElementOB) { //Single frame, non-encapsulated. Pull the data out of the OB element. byte[] inData = (byte[])((DICOMElementOB)pixelDataElem).Data; byte[] uncompressedData = UncompressData(inData, data.TransferSyntax.Compression, imWidth, imHeight, bitsAllocated, samplesPerPixel, planarConfiguration, ybr); if (uncompressedData != null) { //Set new data and change transfer syntax. Good to go! pixelDataElem.Data = uncompressedData; data.TransferSyntax = TransferSyntaxes.ExplicitVRLittleEndian; } } else if (pixelDataElem is DICOMElementSQ) { //Encapsulated, and potentially multiframe... DICOMElementSQ sqElem = (DICOMElementSQ)pixelDataElem; bool didUncompress = false; if (!hasNumFramesTag && sqElem.Items.Count > 1) { // Image has multiple sequence items, but is not multiframe -- must require concatenation. // In the past this path was used for JPEGLS, but I think this is the correct IF check for it. int totalLen = 0; for (int i = 1; i < sqElem.Items.Count; i++) { totalLen += sqElem.Items[i].EncapsulatedImageData.Length; } byte[] compressedData = new byte[totalLen]; int bytePtr = 0; for (int i = 1; i < sqElem.Items.Count; i++) { SQItem sqItem = sqElem.Items[i]; Array.Copy(sqItem.EncapsulatedImageData, 0, compressedData, bytePtr, sqItem.EncapsulatedImageData.Length); bytePtr += sqItem.EncapsulatedImageData.Length; } byte[] uncompressedData = UncompressData(compressedData, data.TransferSyntax.Compression, imWidth, imHeight, bitsAllocated, samplesPerPixel, planarConfiguration, ybr); if (uncompressedData != null) { //Remove image encapsulation //Make a new image data element for the data! DICOMElementOB newElem = new DICOMElementOB(0x7FE0, 0x0010); newElem.Data = uncompressedData; //Store it back to the DICOMData, overwriting the original... data[DICOMTags.PixelData] = newElem; didUncompress = true; } } else { //Iterate through frames. Skip the first pseudo-frame since it's just a lookup entry. for (int i = 1; i < sqElem.Items.Count; i++) { SQItem sqItem = sqElem.Items[i]; byte[] inData = null; if (sqItem.IsEncapsulatedImage) { inData = sqItem.EncapsulatedImageData; } else if (sqItem.Elements.Count > 0) { DICOMElement innerElem = sqItem.Elements[0]; if (innerElem != null && innerElem is DICOMElementOB) { inData = (byte[])((DICOMElementOB)innerElem).Data; } } //Process frame if (inData != null) { byte[] uncompressedData = UncompressData(inData, data.TransferSyntax.Compression, imWidth, imHeight, bitsAllocated, samplesPerPixel, planarConfiguration, ybr); if (uncompressedData != null) { if (numFrames > 1) { //Image is multiframe, so just update the encapsulated data to have the new frame sqItem.EncapsulatedImageData = uncompressedData; didUncompress = true; } else { //Remove image encapsulation //Make a new image data element for the data! DICOMElementOB newElem = new DICOMElementOB(0x7FE0, 0x0010); newElem.Data = uncompressedData; //Store it back to the DICOMData, overwriting the original... data[DICOMTags.PixelData] = newElem; didUncompress = true; } } } } } if (didUncompress) { //Set the transfer syntax to uncompressed! data.TransferSyntax = TransferSyntaxes.ExplicitVRLittleEndian; } } if (samplesPerPixel == 3) { //check for needed colorspace conversion if (ybr) { data[DICOMTags.PhotometricInterpretation].Data = "RGB"; } } } return(true); }
internal static bool Compress(DICOMData data, TransferSyntax newSyntax) { if (data.TransferSyntax.Compression != CompressionInfo.None) { return(false); } if (!SupportsCompression(data.TransferSyntax.Compression)) { return(false); } //Get the pixel data element, and make sure it's OB if (!data.Elements.ContainsKey(DICOMTags.PixelData)) { return(false); } DICOMElement testElem = data.Elements[DICOMTags.PixelData]; //Figure out bit depth DICOMElement bitsAllocatedElem = data.Elements[DICOMTags.BitsAllocated]; int bitsAllocated = int.Parse(bitsAllocatedElem.Display); //Prepare for compression ushort imWidth = (ushort)data.Elements[DICOMTags.ImageWidth].Data; ushort imHeight = (ushort)data.Elements[DICOMTags.ImageHeight].Data; int frameSize = imWidth * imHeight * (bitsAllocated / 8); bool multiFrame = (data.Elements.ContainsKey(DICOMTags.NumberOfFrames)); int numFrames = 1; if (multiFrame && int.TryParse((string)data.Elements[DICOMTags.NumberOfFrames].Data, out numFrames)) { } int samplesPerPixel = (data.Elements.ContainsKey(DICOMTags.SamplesPerPixel) ? (int)(ushort)data.Elements[DICOMTags.SamplesPerPixel].Data : 1); int planarConfiguration = (data.Elements.ContainsKey(DICOMTags.PlanarConfiguration) ? (int)(ushort)data.Elements[DICOMTags.PlanarConfiguration].Data : 0); //Quick type test to make sure encapsulation is correct if (multiFrame && (testElem is DICOMElementSQ) && (((List <SQItem>)testElem.Data).Count < numFrames + 1)) { return(false); } if (!multiFrame && !(testElem is DICOMElementOB)) { return(false); } //Encapsulate the new element DICOMElementSQ newData = new DICOMElementSQ(0x7FE0, 0x0010); //Create the pointer lookup SQItem lookupItem = new SQItem(newData); newData.Items.Add(lookupItem); byte[] lookupData = new byte[4 * numFrames]; lookupItem.EncapsulatedImageData = lookupData; int dataPtr = 0; for (int i = 0; i < numFrames; i++) { //Add pointer to lookup Array.Copy(BitConverter.GetBytes(dataPtr), 0, lookupData, 4 * i, 4); byte[] inData; int startPtr; if (testElem is DICOMElementSQ) { inData = ((List <SQItem>)testElem.Data)[i + 1].EncapsulatedImageData; startPtr = 0; } else { inData = (byte[])testElem.Data; startPtr = i * frameSize; } //Compress data and add to new encapsulated item byte[] outData = null; unsafe { if (newSyntax.Compression == CompressionInfo.JPEG2000) { fixed(byte *pInData = inData) { int lenOut = 0; byte *dataOut = (byte *)CompressJ2K((IntPtr)(pInData + startPtr), bitsAllocated, imWidth, imHeight, samplesPerPixel, planarConfiguration, ref lenOut); outData = new byte[lenOut]; Marshal.Copy((IntPtr)dataOut, outData, 0, lenOut); FreePtr((IntPtr)dataOut); } } else if (newSyntax.Compression == CompressionInfo.JPEGLossless || newSyntax.Compression == CompressionInfo.JPEGLossy) { fixed(byte *pInData = inData) { int compressionMode = 0; //0 = baseline, 1 = extended sequential, 2 = spectralselec, 3 = progressive, 4 = lossless int firstOrder = 1; //1 = first order, 0 = not? 0 seems to be rejected. int pointTrans = 0; //0 is the default point transformation... don't know how to use this... if (newSyntax == TransferSyntaxes.JPEGBaselineProcess1) { compressionMode = 0; //baseline } else if (newSyntax == TransferSyntaxes.JPEGExtendedProcess24) { compressionMode = 1; //extended? } else if (newSyntax == TransferSyntaxes.JPEGLosslessNonHierarchicalFirstOrderPredictionProcess14) { compressionMode = 4; firstOrder = 1; pointTrans = 0; } //lossless first order else if (newSyntax == TransferSyntaxes.JPEGLosslessNonHierarchicalProcess14) { compressionMode = 4; firstOrder = 1; pointTrans = 0; } //lossless non-first order int lenOut = 0; byte *dataOut = (byte *)CompressJPEG((IntPtr)(pInData + startPtr), bitsAllocated, imWidth, imHeight, samplesPerPixel, planarConfiguration, compressionMode, firstOrder, pointTrans, ref lenOut); outData = new byte[lenOut]; Marshal.Copy((IntPtr)dataOut, outData, 0, lenOut); FreePtr((IntPtr)dataOut); } } else if (newSyntax.Compression == CompressionInfo.JPEGLSLossless || newSyntax.Compression == CompressionInfo.JPEGLSLossy) { fixed(byte *pInData = inData) { int compressionMode = (newSyntax.Compression == CompressionInfo.JPEGLSLossless) ? 0 : 1; //allowed difference: 0 = lossless, >=1 = lossy -- >1 seems to crash it... int lenOut = 0; byte *dataOut = (byte *)CompressJPEGLS((IntPtr)(pInData + startPtr), bitsAllocated, imWidth, imHeight, samplesPerPixel, planarConfiguration, compressionMode, ref lenOut); outData = new byte[lenOut]; Marshal.Copy((IntPtr)dataOut, outData, 0, lenOut); FreePtr((IntPtr)dataOut); } } } if (outData == null) { return(false); } //Add new image data sequence item SQItem item = new SQItem(newData); item.EncapsulatedImageData = outData; newData.Items.Add(item); //Update pointer for lookup dataPtr += outData.Length; } data[DICOMTags.PixelData] = newData; data.TransferSyntax = newSyntax; if (samplesPerPixel == 3) { //check for needed colorspace conversion string photoInterp = (string)data[DICOMTags.PhotometricInterpretation].Data; if (photoInterp == "RGB") { data[DICOMTags.PhotometricInterpretation].Data = "YBR_FULL_422"; } } return(true); }
/// <summary> /// Adds a DICOM object to the collection /// </summary> /// <param name="d">the DICOM object to be added</param> public void AddObject(DICOMElement d) { collection.Add(d); }
/// <summary> /// Adds a Dicom object to the DicomObject stack and sorts by tag. /// </summary> /// <param name="d">the Dicom object to be added</param> public void AddObject(DICOMElement d) { //Add object to list this.DicomObjects.Add(d); //Sort List by ID this.DicomObjects.Sort(delegate(DICOMElement d1, DICOMElement d2) { return d1.Tag.Id.CompareTo(d2.Tag.Id); }); }