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); }