Introduction
First of all, by writing this article, I am celebrating my success on my assignment to handle Dicom files in MVC. There are many software available which can show Dicom images but it was a web application and browser doesn't understand DICOM format, so I had to process the uploaded zip and convert each DICOM file to Jpeg format and show these on the browser. Though there are many projects on the internet depicting conversion of Dicom files to Standard image formats (Code Project also has an aritcle), none of the projects fulfilled my requirements because some use deprecated DLLs or some are bounded with the Dicom extension (.dcm). So I created a project which can convert any Dicom file (with or without extension) to standard image format.
Background
Developers having knowledge of Dicom or having challenges regarding Dicom library would find it interesting.
Steps to Run the Project
Open this project using Visual Studio 2012 or 2013 and run it. It will show you the Index page of DicomSlider Controller. You will see a file upload control, click the browse button and browse to project folder itself, it has a folder named "Sample Files", open this folder you will find a zip file, select it and click on submit button. After clicking submit button, it will process each file of zip folder and convert it to jpeg file and show you on the same Index page. Now let's look at what exactly is being done at code level to achieve this.
Using the Code
It is a simple MVC project without Authentication having a single controller and two actions and a single view. You will need two DLLs to copy in your project to make this code work. I am posting the full project here so you can find those DLLs in project's bin folder named: "dicom.dll and dicom.native.dll".
So basically, this project has a view which is capable of uploading a zip file and this same view also shows the result of zip processing. It displays the images which are converted from Dicom format to JPEG. So let's look at the code step by step.
1. View (Index.cshtml)
The view is very simple having only a browse button to upload the zip of Dicom files and after submit, it will show the converted files:
@model List<DicomSliderDemo.Models.SliderModel>
@{
ViewBag.Title = "Index";
}
@using (Html.BeginForm("UploadZip", "DicomSlider",
FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<table>
<tr>
<input id="filePosted" name="filePosted" type="file" />
<input id="Submit1" type="submit" value="submit" />
</tr>
So you can see this view is having a model of list type, this list will be responsible for showing the converted images. It has a from which is being post at "UploadZip
" action of "DicomSlider
" controller. So when you click submit button, the form posted to this action with the selected files in file upload control. We have a Div
under the form
element, this div
will be responsible to show the converted files. You can see an img
tag there having the src
pointing to "DICOMIMG" folder which resides in our root project files. You will see how this folder is used to store images at the controller level so let's look at the Controller Code, basically we will look at the "UploadZip
" action.
2. Controller
public class DicomSliderController : Controller
{
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult UploadZip(HttpPostedFileBase filePosted)
{
List<SliderModel> objModel = new List<SliderModel>();
try
{
objModel=uploadAndSaveZipFile(filePosted);
}
catch (Exception ex)
{ }
return View("Index", objModel);
}
public List<SliderModel> uploadAndSaveZipFile(HttpPostedFileBase objFile)
{
List<SliderModel> objModel = new List<SliderModel>();
try
{
string FilesPath = Server.MapPath("Dtest");
string ImagesPath = Server.MapPath("DICOMIMG");
DirectoryInfo objDirectory = new DirectoryInfo(FilesPath);
DirectoryInfo objDirectoryImg = new DirectoryInfo(ImagesPath);
if (!objDirectoryImg.Exists)
{
Directory.CreateDirectory(ImagesPath);
}
if (objDirectory.Exists)
{
foreach (System.IO.DirectoryInfo subDirectory in objDirectory.GetDirectories())
{
SetDirAttributesNormal(subDirectory);
subDirectory.Delete(true);
}
SetDirAttributesNormal(objDirectory);
objDirectory.Delete(true);
Directory.CreateDirectory(Server.MapPath("Dtest"));
}
ZipFile objZip = ZipFile.Read(objFile.InputStream);
objZip.ExtractAll(FilesPath);
string[] objFInfo = Directory.GetFiles(FilesPath +
"\\" + Path.GetFileNameWithoutExtension(objFile.FileName));
int i = 0;
foreach (var ofile in objFInfo)
{
try
{
var img = new DicomImage(ofile);
Bitmap bitmap = new Bitmap(img.RenderImage());
bitmap.Save(ImagesPath + "\\D" + i + ".jpg", ImageFormat.Jpeg);
i++;
}
catch (Exception ex)
{
}
}
objDirectory = new DirectoryInfo(FilesPath);
if (objDirectory.Exists)
{
foreach (System.IO.DirectoryInfo subDirectory in objDirectory.GetDirectories())
{
SetDirAttributesNormal(subDirectory);
subDirectory.Delete(true);
}
SetDirAttributesNormal(objDirectory);
objDirectory.Delete(true);
}
objDirectoryImg = new DirectoryInfo(ImagesPath);
objModel = objDirectoryImg.GetFiles().Select(x => new SliderModel
{
Id=x.Name,
Path = x.Name,
}).ToList();
}
catch (Exception ex)
{ }
return objModel;
}
[NonAction]
public void SetDirAttributesNormal(DirectoryInfo dir)
{
foreach (var subDirPath in dir.GetDirectories())
{
SetDirAttributesNormal(new DirectoryInfo(subDirPath.ToString()));
subDirPath.Attributes = FileAttributes.Normal;
}
foreach (var filePath in dir.GetFiles())
{
var file = new FileInfo(filePath.Directory + "/" + filePath.Name);
file.Attributes = FileAttributes.Normal;
}
}
}
You can see the Controller
has four methods, I will describe it step by step.
- Index Action: It is responsible for the page which gives us the facility to upload files and see the results in the form of image files.
- UploadZip Action: This is the post action which will be called when you click the submit button on Index page. This is the action which is responsible for our core functionality, it processes the zip file and converts the DICOM files to JPEG files. So let's look at the important line of codes of this action.
This action is expecting the "HttpPostedFileBase
" object which is standard object of System.Web
namespace that contains the uploaded file by the user.
It is declaring a list of "SliderModel
". If you look at this class, then you can find two properties in it:
Where Id
depicts the image name and Path
depicts the physical file name. In our case, both are the same.
Next, it calls a function named "uploadAndSaveZipFile
" and passes the object of "HttpPostedFileBase
" and this function returns a list of "SliderModel
" at last it returns the "Index
" view with "SliderModel
" and we already decorated our Index
view with this kind of model so it can handle it.
- uploadAndSaveZipFile Method: This method extracts the zip and processes the files one by one to convert these in jpeg file format. This function deletes any zip file with the same name (if it exists) before extracting the uploaded zip and in the end, it gives a list of converted image's paths and names. So let's look at the important code lines of this method.
So in the first two lines, it sets the paths of extracted zip file and folder to store converted images fields respectively. In "Dtest" directory, it will extract the zip file and "DICOMIMG" folder will hold the converted images.
So first, we checked if Image Directory exists or not and if it doesn't exist, then we created it. It is a simple code just dealing with "DirectoryInfo
" class.
Next, it checks if "Dtest" directory exists and if it exists, then it deletes the folder and its files because it may have old files. It first loops through sub directories and deletes all, then it deletes the root folder. One thing to note before deletion is that it calls a function "SetDirAttributesNormal
". This function basically sets file attribute to Normal
in case if it is a "ReadOnly
" file because if the file is readonly, then it can't be deleted, you have to change its attribute first. After deleting the existing directory, it creates a new directory.
Next, it creates an object of "ZipFile
" by passing input stream of uploaded file. This call is taken from Ionic.Zip.dll. After creating object, it extract all the files of zip to "Dtest" folder.
Next, it fetches the list of all files in "Dtest" folder and it extracts the file names without extension. It loops through the list and now the conversion part comes:
var img = new DicomImage(ofile);
Bitmap bitmap = new Bitmap(img.RenderImage());
bitmap.Save(ImagesPath + "\\D" + i + ".jpg", ImageFormat.Jpeg);
In the first line, it creates an object of "DicomImage
" class, the constructor takes file name and returns an object of "DicomImage
" class. In the second line, we create an object of Bitmap
class, the constructor takes "Image
" as an argument, so we use the "DicomImage
" object and calls the method called "RenderImage
" which returns an object of "Image
". Then, in the last line, it saves the image in the format of jpeg in "DICOMIMG" folder.
Now, it deletes the "Dtest" directory and returns the list of converted image's name.
This is how it converts Dicom files to JPEG files.
The submitted code can be rough somewhere, so sorry for that, but it will surely solve your challenges.
Thanks for reading the article, I have attached the sample project so you can download and check. If you have any questions regarding this article, I am here to solve those anytime.