LAUREL BRIDGE

LaurelBridge.DCFExamples.CodecEncodeJ2K91 Namespace

DICOM Connectivity Framework V3.4
The CodecEncodeJ2K91 example demonstrates use of the IFrameProducer interface for getting raw pixel data, and how to configure DCF for lossy and lossless encoding of the J2K91 transfer syntax (1.2.840.10008.1.2.4.91).
Classes

  ClassDescription
Public classProgram
The DICOM standard allows the 1.2.840.10008.1.2.4.91 J2K91 transfer syntax to be encoded lossy or losslessly.

An astute DCF customer noticed that our J2K91 implementation did not allow a lossless encoding. The codec options were enhanced to allow the selection of lossless encoding (lossy is still the default for J2K91). This example demonstrates how to use DCF to configure lossy and lossless encoding of the J2K91 transfer syntax.

This example also demonstrates usage of the IFrameProducer interface for extracting pixel data. The image pixels from each encoding are acquired and compared to the original image to verify expected behavior.
Remarks

Supported OS Platforms:

  • Windows - .Net Framework 4.7.2 64-bit and 32-bit
  • Windows - .Net Core 2.1 64-bit and 32-bit
  • Linux - .Net Core 2.1 64-bit

Examples

CodecEncodeJ2K91 Sample Code
public class Program
{
    /// <summary>
    /// Main entry point for CodecEncodeJ2K91.
    /// </summary>
    [STAThread]
    public static void Main()
    {
        try
        {
            WriteAndCompareJ2K91Encodings();
        }
        catch (Exception e)
        {
            Console.WriteLine("Exception caught during execution: {0}", e);
            Environment.ExitCode = 1;
        }

        if (System.Diagnostics.Debugger.IsAttached)
        {
            Console.Write("Press any key to continue . . . ");
            Console.ReadKey();
        }
    }

    /// <summary>
    /// Use mr-knee.dcm encoded as 1.2.840.10008.1.2 as an image source to produce a copy 
    /// named mr-knee-j2k91-lossy-compressed.dcm using 1.2.840.10008.1.2.4.91 lossy compression,
    /// and another copy named mr-knee-j2k91-lossless-compressed.dcm using 1.2.840.10008.1.2.4.91
    /// lossless compression.
    /// </summary>
    private static void WriteAndCompareJ2K91Encodings()
    {
        const string originalImageName = "mr-knee.dcm";
        const string j2KLossyImageName = "mr-knee-j2k91-lossy-compressed.dcm";
        const string j2KLosslessImageName = "mr-knee-j2k91-lossless-compressed.dcm";
        bool errorOccurred = false;

        Console.WriteLine("Use the ImageViewer example to inspect the files produced vs original");
        // The default session encodes 1.2.840.10008.1.2.4.91 lossy.
        WriteDicomJ2K91File(originalImageName, j2KLossyImageName, null);

        // configure the standard Jpeg 2000 codec to encode 1.2.840.10008.1.2.4.91 lossless.
        JasperCodecOptions jasperOptions = new JasperCodecOptions(Uids.J2k91) { CompressionMode = 0 };
        DicomSessionSettings sessionSettings = new DicomSessionSettings();
        sessionSettings.SetCodecOptions(jasperOptions);
        WriteDicomJ2K91File(originalImageName, j2KLosslessImageName, sessionSettings);

        // Compare results
        Array originalImagePixels = GetPixels(originalImageName);
        Array j2K91LossyPixels = GetPixels(j2KLossyImageName);
        Array j2K91LosslessPixels = GetPixels(j2KLosslessImageName);

        if (ImageValuesEqual(originalImagePixels, j2K91LossyPixels))
        {
            Console.WriteLine("Error: expected lossy compression");
            errorOccurred = true;
        }
        if (!ImageValuesEqual(originalImagePixels, j2K91LosslessPixels))
        {
            Console.WriteLine("Error: expected lossless compression");
            errorOccurred = true;
        }
        if (!errorOccurred)
            Console.WriteLine("Image compression performed as expected");
    }

    /// <summary>
    /// Compare 2 arrays of image pixel values.
    /// </summary>
    /// <remarks>
    /// The performance of this example routine is not optimal because boxing and unboxing 
    /// occurs at the pixel level.
    /// </remarks>
    /// <param name="expected">The expected image values</param>
    /// <param name="actual">The actual image values</param>
    /// <returns>true if all array values are equal</returns>
    private static bool ImageValuesEqual(Array expected, Array actual)
    {
        if (expected.Length != actual.Length)
            return false;

        for (int i = 0; i < expected.Length; i++)
        {
            if (!expected.GetValue(i).Equals(actual.GetValue(i)))
                return false;
        }
        return true;
    }

    /// <summary>
    /// Return the array of pixels for frame 0 from the given imagePath.
    /// </summary>
    /// <param name="imagePath">the path</param>
    /// <returns>The first frame as an array of pixels.</returns>
    private static Array GetPixels(string imagePath)
    {
        using (FrameProducer fp = FrameProducer.CreateFromFile(imagePath))
        {
            if (!fp.IsValid())
                throw new InvalidOperationException("FrameProducer is invalid: " + fp);
            Array pixels = null;
            if (!fp.GetRawPixels(0, ref pixels))
                throw new IOException("GetRawPixels failed for " + imagePath);
            // this should never happen
            if (pixels.GetType().GetElementType() != fp.RawPixelType)
                throw new InvalidOperationException("pixel element type invalid");
            // this should never happen either
            if (pixels.Length != fp.ImageInfo.FrameSize / Marshal.SizeOf(fp.RawPixelType))
                throw new InvalidOperationException("frame size invalid");
            return pixels;
        }
    }

    /// <summary>
    /// Read Dicom file and output its contents using 1.2.840.10008.1.2.4.91
    /// </summary>
    /// <param name="inName">The Dicom file name to read</param>
    /// <param name="outName">The Dicom file name to write</param>
    /// <param name="sessionSettings">The setting used to influence the nature of writing and possibly reading
    /// inName and outName</param>
    private static void WriteDicomJ2K91File(string inName, string outName, DicomSessionSettings sessionSettings)
    {
        using (DicomFileInput dfi = new DicomFileInput(inName))
        {
            DicomDataSet ds = dfi.ReadDataSet();
            using (DicomFileOutput dfo = new DicomFileOutput(outName, Uids.J2k91, sessionSettings))
            {
                dfo.WriteDataSet(ds);
                Console.WriteLine("({0}) written to ({1}) using ({2}).", inName, outName, Uids.J2k91);
            }
        }
    }
}