Encode JPG image file as DICOM PixelData using ClearCanvas - c#

I have a set of JPG images that are actually slices of a CT scan, which I want to reconstruct into DICOM image files and import into a PACS.
I am using ClearCanvas, and have set all of the requisite tags (and confirmed them by converting one of my JPG files to DICOM using a proprietary application to make sure they are the same). I am just not sure how I should be processing my JPG file to get it into the PixelData tag?
Currently I am converting it to a Byte array, on advice from ClearCanvas forums, but the image is just garbled in the DICOM viewer. How should I be processing the image data to get it into a readable format?
public DicomFile CreateFileFromImage(Image image)
{
int height = image.Height;
int width = image.Width;
short bitsPerPixel = (short)Image.GetPixelFormatSize(image.PixelFormat);
byte[] imageBuffer = ImageToByteArray(image);
DicomFile dicomFile = new DicomFile();
dicomFile.DataSet[DicomTags.Columns].SetInt32(0, width);
dicomFile.DataSet[DicomTags.Rows].SetInt32(0, height);
dicomFile.DataSet[DicomTags.BitsStored].SetInt16(0, bitsPerPixel);
dicomFile.DataSet[DicomTags.BitsAllocated].SetInt16(0, bitsPerPixel);
dicomFile.DataSet[DicomTags.HighBit].SetInt16(0, 7);
//other tags not shown
dicomFile.DataSet[DicomTags.PixelData].Values = imageBuffer;
return dicomFile;
}
public static byte[] ImageToByteArray(Image imageIn)
{
MemoryStream ms = new MemoryStream();
imageIn.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
return ms.ToArray();
}

The ClearCanvas library as two helper classes that make it easier to encode and decode pixel data within a DicomFile. They are the DicomCompressedPixelData class and the DicomUncompressedPixelData class. You can use these to set the parameters for the image, and encode them into the DicomFile object.
In your case, since you're encoding a compressed object, you should use the DicomCompressedPixelData class. There are properties on the class that can be set. Calling the UpdateMessage method will copy these property values over to the DicomFile object. Also, this class has an AddFrameFragment method that properly encodes the pixel data. Note that compressed pixel data has to have some specific binary wrappers around each frame of data. This was the part missing from your previous code. The code below shows how to set this up.
short bitsPerPixel = (short)Image.GetPixelFormatSize(image.PixelFormat);
var dicomFile = new DicomFile();
var pd = new DicomCompressedPixelData(dicomFile);
pd.ImageWidth = (ushort)image.Width;
pd.ImageHeight = (ushort) image.Height;
pd.BitsStored = (ushort)bitsPerPixel;
pd.BitsAllocated = (ushort) bitsPerPixel;
pd.HighBit = 7;
pd.SamplesPerPixel = 3;
pd.PlanarConfiguration = 0;
pd.PhotometricInterpretation = "YBR_FULL_422";
byte[] imageBuffer = ImageToByteArray(image);
pd.AddFrameFragment(imageBuffer);
pd.UpdateMessage(dicomFile);
return dicomFile;

I ended up processing the bitmap manually and creating an array out of the Red Channel in the image, following some code in a plugin:
int size = rows * columns;
byte[] pData = new byte[size];
int i = 0;
for (int row = 0; row < rows; ++row)
{
for (int column = 0; column < columns; column++)
{
pData[i++] = image.GetPixel(column, row).R;
}
}
It does work, but it is horribly slow and creates bloated DICOM files. I'd love to get the inbuilt DicomCompressedPixelData class working.
Any further suggestions would be very welcome.

It is important to know the bit depth and color components of your JPEG CT image before inserting into DICOM dataset. It could be 8-bit lossy (JPEG Compression Process 2) or 12-bit lossy (JPEG Compression Process 4) or 8, 12 or 16 bit lossless grayscale JPEG (JPEG Compression Process 14 - lossless, non-hierarchical). This information is critical for updating the Pixel Data related information such as Photometric Interpretation, Sample per Pixel, Planer Configuration, Bits Allocated, High Bit to Transfer Syntax.

Related

Way to decode an image into a thumbnail, skipping loading the image completely

I am currently trying to create thumbnails the moment I am decoding them to avoid loading the entire image into memory and then scaling it. Secondly I want to get rid from my other thumbnail code which is using ShellObjects and what the OS file explorer cached as thumbnails. The problem with the later is that its depending on if there is anything cached.
The following code is my attempt to create an image the moment its decoded which fails with a Format Unknown error. I am pretty close to have the solution so I am coming here because I have not found an answer. Every "solution" I found loaded the entire file, creating two images, scaling the original which creates more overhead than what I believe is needed to accomplish this. Pretty resource intensive for an image manager with a thousand image files being loaded asynchronously.)
public static async Task<Texture2D> GetThumbnail(string filePath)
{
// Decode the image directly in the given DecodePixelHeight (or width), maintaining aspect ratio.
var thumbnail = new BitmapImage();
thumbnail.BeginInit();
thumbnail.UriSource = new Uri(filePath, UriKind.Absolute);
thumbnail.DecodePixelHeight = 144; //Fit to this height.
thumbnail.EndInit();
// Here I am trying reset a format. I am aiming to not need this step.
// Format the bitmap image into a known format.
var formatted = new FormatConvertedBitmap();
formatted.BeginInit();
formatted.Source = thumbnail;
formatted.DestinationFormat = System.Windows.Media.PixelFormats.Default;
formatted.EndInit();
using var stream = new MemoryStream();
var bytesPerPixel = (formatted.DestinationFormat.BitsPerPixel + 7) / 8;
var stride = 4 * ((formatted.PixelWidth * bytesPerPixel + 3) / 4);
var buffer = new byte[formatted.PixelHeight * stride];
formatted.CopyPixels(buffer, stride, 0);
await stream.WriteAsync(buffer, 0, buffer.Length);
return Texture2D.FromStream(___.GraphicsDevice, stream);
}

Getting EMGU Image from png bytes

I'm trying get an Image from png bytes.
In the following example I am able to load the png from the file, but not from the file's bytes:
//using Emgu.CV;
//using Emgu.CV.Structure;
var path = #"C:\path\to\my.png";
// Using the constructor with the file path works great!
var imageFromFile = new Image<Rgb, byte>(path);
var bytes = File.ReadAllBytes(path);
var size = imageFromFile.Size;
var imageFromBytes = new Image<Rgb, byte>(size);
// Assigning the bytes ails with an 'ArgumentOutOfRangeException' exception
imageFromBytes.Bytes = bytes;
This actually makes sense, since imageFromBytes.Bytes expects an array with size of:
size.Width * size.Height * 3
while the bytes array has the size of the compressed png image.
So, my question is - Given a png byte array, how can I get an instance of an Image of those bytes?
After poking around a little in the Emgu github repo, the following will get the image from the byte array.
Not so OOP-ish, but works...
var imageFromBytes = new Image<Rgb, byte>(imageFromFile.Size);
CvInvoke.Imdecode(bytes, Emgu.CV.CvEnum.ImreadModes.AnyColor, imageFromBytes.Mat);
// Now imageFromBytes holds the uncompressed bytes.

Convert TIFF to 1bit

I wrote a desktop app which converts an 8bit TIFF to a 1bit but the output file cannot be opened in Photoshop (or other graphics software).
What the application does is
it iterates every 8 bytes (1 byte per pixel) of the original image
then converts each value to bool (so either 0 or 1)
saves every 8 pixels in a byte - bits in the byte are in the same order as the pixels in the original image
The TIFF tags I set: MINISBLACK, compression is NONE, fill order is MSB2LSB, planar config is contiguous. I'm using BitMiracle's LibTiff.NET for reading and writing the files.
What am I doing wrong that the output cannot be opened by popular software?
Input image: http://www.filedropper.com/input
Output image: http://www.filedropper.com/output
From your description of the byte manipulation part, it appears you are converting the image data from 8-bit to 1-bit correctly.
If that's the case, and you don't have specific reasons to do it from scratch using your own code, you can simplify the task of creating valid TIFF files by using System.Drawing.Bitmap and System.Drawing.Imaging.ImageCodecInfo. This allows you to save either uncompressed 1-bit TIFF or compressed files with different types of compression. The code is as follows:
// first convert from byte[] to pointer
IntPtr pData = Marshal.AllocHGlobal(imgData.Length);
Marshal.Copy(imgData, 0, pData, imgData.Length);
int bytesPerLine = (imgWidth + 31) / 32 * 4; //stride must be a multiple of 4. Make sure the byte array already has enough padding for each scan line if needed
System.Drawing.Bitmap img = new Bitmap(imgWidth, imgHeight, bytesPerLine, PixelFormat.Format1bppIndexed, pData);
ImageCodecInfo TiffCodec = null;
foreach (ImageCodecInfo codec in ImageCodecInfo.GetImageEncoders())
if (codec.MimeType == "image/tiff")
{
TiffCodec = codec;
break;
}
EncoderParameters parameters = new EncoderParameters(2);
parameters.Param[0] = new EncoderParameter(Encoder.Compression, (long)EncoderValue.CompressionLZW);
parameters.Param[1] = new EncoderParameter(Encoder.ColorDepth, (long)1);
img.Save("OnebitLzw.tif", TiffCodec, parameters);
parameters.Param[0] = new EncoderParameter(Encoder.Compression, (long)EncoderValue.CompressionCCITT4);
img.Save("OnebitFaxGroup4.tif", TiffCodec, parameters);
parameters.Param[0] = new EncoderParameter(Encoder.Compression, (long)EncoderValue.CompressionNone);
img.Save("OnebitUncompressed.tif", TiffCodec, parameters);
img.Dispose();
Marshal.FreeHGlobal(pData); //important to not get memory leaks

PhotoCaptureDevice in native code

On Windows Phone 8, I wish to take a camera shot in native code, but I'm blocked on the final stage not being able to extract information from IOutputStream.
in C# we code:
MemoryStream image = new MemoryStream();
MemoryStream imagePreview = new MemoryStream();
cameraCaptureSequence.Frames[0].CaptureStream = image.AsOutputStream();
cameraCaptureSequence.Frames[0].ThumbnailStream = imagePreview.AsOutputStream();
await cameraCaptureSequence.StartCaptureAsync();
from now image stream has information of captured image and I can render it.
In C++ / Cx I need to do the same thing but more until to catch the byte* of captured image, here my code:
Windows::Phone::Media::Capture::CameraCaptureSequence^ cameraCaptureSequence;
IBuffer^ image;
return concurrency::create_async([this]()
{
cameraCaptureSequence->Frames->GetAt(0)->CaptureStream = reinterpret_cast<IOutputStream^>(image);
create_task( m_camera->PrepareCaptureSequenceAsync(cameraCaptureSequence) ).wait();
create_task( cameraCaptureSequence->StartCaptureAsync() ).then([this]()
{
}
}
Starting from the most basic thing I wish to understand how to "save" into an IBuffer^ the result of captured image stream, better how to get the internal byte* buffer.
Thanks
You can access the pixel data from a captured image in Native code through the ICameraCaptureFrameNative. The object that implements the interface is obtained through COM. Once you have obtained the object, use MapBuffer() to access the BYTE * array.
Note that the pixel data obtained this way is in NV12 format, not a JPEG or RGB as one would expect.
#include <Windows.Phone.Media.Capture.Native.h>
CameraCaptureFrame^ frame = m_cameraCaptureSequence->Frames->GetAt(0);
pNativeFrame = NULL;
HRESULT hr = reinterpret_cast<IUnknown*>(frame)->QueryInterface(__uuidof(ICameraCaptureFrameNative ), (void**) &pNativeFrame);
create_task( m_camera->PrepareCaptureSequenceAsync(m_cameraCaptureSequence) ).wait();
create_task( m_cameraCaptureSequence->StartCaptureAsync() ).then([this]()
{
DWORD bufferSize =0;
BYTE * pBuffer = NULL;
pNativeFrame->MapBuffer(&bufferSize, &pBuffer); // Pixels are in pBuffer.
// Unmap() the buffer before capturing another image.
ICameraCaptureFrameNative doesn't give access to a texture containing the preview?
If you want acces data from a IBuffer look here : http://msdn.microsoft.com/en-us/library/windows/apps/dn182761.aspx
For your case, i thinks you need a class which implement IOutputStream. Maybe InMemoryRandomAccessStream ?

Exctract FlateDecode images using iTextSharp

I want to extract images from an PDF. I'm using iTextSharp right now.
Some images can be extracted correct, but most of them don't have the right colors and are distorted.
I did some experiments with different PixelFormats, but I didn't get a solution for my Problem...
This is the Code which separates the image-types:
if (filter == "/FlateDecode")
{
// ...
int w = int.Parse(width);
int h = int.Parse(height);
int bpp = tg.GetAsNumber(PdfName.BITSPERCOMPONENT).IntValue;
byte[] rawBytes = PdfReader.GetStreamBytesRaw((PRStream)tg);
byte[] decodedBytes = PdfReader.FlateDecode(rawBytes);
byte[] streamBytes = PdfReader.DecodePredictor(decodedBytes, tg.GetAsDict(PdfName.DECODEPARMS));
PixelFormat[] pixFormats = new PixelFormat[23] {
PixelFormat.Format24bppRgb,
// ... all Pixel Formats
};
for (int i = 0; i < pixFormats.Length; i++)
{
Program.ToPixelFormat(w, h, pixFormats[i], streamBytes, bpp, images));
}
}
This is the Code to save the Image in a MemoryStream. Saving the image in a folder is implemented later.
private static void ToPixelFormat(int width, int height, PixelFormat pixelformat, byte[] bytes, int bpp, IList<Image> images)
{
Bitmap bmp = new Bitmap(width, height, pixelformat);
BitmapData bmd = bmp.LockBits(new Rectangle(0, 0, width, height),
ImageLockMode.WriteOnly, pixelformat);
Marshal.Copy(bytes, 0, bmd.Scan0, bytes.Length);
bmp.UnlockBits(bmd);
using (var ms = new MemoryStream())
{
bmp.Save(ms, System.Drawing.Imaging.ImageFormat.Tiff);
bytes = ms.GetBuffer();
}
images.Add(bmp);
}
Please help me.
even you found solution for your problem, let me say suggestion to fix your code above.
I believe the distortion problem is caused because of mismatch in row data boundary. PdfReader returns data in a byte boundary. For example for grayscale image 20 pixel wide you will get 20 bytes of data for each image row. Bitmap class works with 32bit boundary. When creating bitmap with 20 pixels of width, Bitmap class will generate grayscale bitmap with stride(byte width)=32 bytes. It means you cannot simply copy the retrieved bytes from PdfReader into a new bitmap using Marshal.Copy() method as it is in your ToPixelFormat().
First pixel in source byte array is located as 21st byte but destination Bitmap needs it as 33rd byte becasue of the 32bit boundary of the Bitmap. To solve this issue I had to create byte array with size that considers the 32bit boundary for each data row.
Copy data row by row from bytes aray retrieved from PdfReader into new byte array with 32bit row boundary consideration. Now I had bytes of data with boundary that matched the Bitmap class boundary so I can copy it to the new Bitmap using Marshal.Copy().
I found an solution for my own problem.
To extract all Images on all Pages, it is not necessary to implement different filters.
iTextSharp has an Image Renderer, which saves all Images in their original image type.
Just do the following found here: http://kuujinbo.info/iTextSharp/CCITTFaxDecodeExtract.aspx
You don't need to implement HttpHandler...
PDF supports a pretty wide variety of image formats. I don't think I would take this approach you've chosen here. You need to determine the image format from the bytes in the stream itself. For example, JPEG will typically start with the ASCII bytes JFIF.
.NET (3.0+) does come with a method that will attempt to pick the right decoder: BitmapDecoder.Create. See http://msdn.microsoft.com/en-us/library/system.windows.media.imaging.bitmapdecoder.aspx
If that doesn't work you may want to consider some third-party imaging libraries. I've used ImageMagick.NET and LeadTools (way overpriced).

Categories