PrintJob.cs 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. // Copyright (c) 2012-2020 fo-dicom contributors.
  2. // Licensed under the Microsoft Public License (MS-PL).
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Drawing;
  6. using System.Drawing.Imaging;
  7. using System.Threading.Tasks;
  8. using Dicom;
  9. using Dicom.Imaging;
  10. using Dicom.IO;
  11. using Dicom.Network;
  12. using Dicom.Printing;
  13. using DicomClient = Dicom.Network.Client.DicomClient;
  14. namespace Print_SCU
  15. {
  16. internal class PrintJob
  17. {
  18. public string CallingAE { get; set; }
  19. public string CalledAE { get; set; }
  20. public string RemoteAddress { get; set; }
  21. public int RemotePort { get; set; }
  22. public FilmSession FilmSession { get; private set; }
  23. private FilmBox _currentFilmBox;
  24. public PrintJob(string jobLabel)
  25. {
  26. FilmSession = new FilmSession(DicomUID.BasicFilmSessionSOPClass)
  27. {
  28. FilmSessionLabel = jobLabel,
  29. MediumType = "PAPER",
  30. NumberOfCopies = 1
  31. };
  32. }
  33. public FilmBox StartFilmBox(string format, string orientation, string filmSize)
  34. {
  35. var filmBox = new FilmBox(FilmSession, null, DicomTransferSyntax.ExplicitVRLittleEndian)
  36. {
  37. ImageDisplayFormat = format,
  38. FilmOrientation = orientation,
  39. FilmSizeID = filmSize,
  40. MagnificationType = "NONE",
  41. BorderDensity = "BLACK",
  42. EmptyImageDensity = "BLACK"
  43. };
  44. filmBox.Initialize();
  45. FilmSession.BasicFilmBoxes.Add(filmBox);
  46. _currentFilmBox = filmBox;
  47. return filmBox;
  48. }
  49. public void AddImage(Bitmap bitmap, int index)
  50. {
  51. if (FilmSession.IsColor)
  52. {
  53. AddColorImage(bitmap, index);
  54. }
  55. else
  56. {
  57. AddGreyscaleImage(bitmap, index);
  58. }
  59. }
  60. private void AddGreyscaleImage(Bitmap bitmap, int index)
  61. {
  62. if (_currentFilmBox == null)
  63. {
  64. throw new InvalidOperationException("Start film box first!");
  65. }
  66. if (index < 0 || index > _currentFilmBox.BasicImageBoxes.Count)
  67. {
  68. throw new ArgumentOutOfRangeException(nameof(index), "Image box index out of range");
  69. }
  70. if (bitmap.PixelFormat != PixelFormat.Format24bppRgb && bitmap.PixelFormat != PixelFormat.Format32bppArgb
  71. && bitmap.PixelFormat != PixelFormat.Format32bppRgb)
  72. {
  73. throw new ArgumentException("Not supported bitmap format", nameof(bitmap));
  74. }
  75. var dataset = new DicomDataset();
  76. dataset.Add<ushort>(DicomTag.Columns, (ushort)bitmap.Width)
  77. .Add<ushort>(DicomTag.Rows, (ushort)bitmap.Height)
  78. .Add<ushort>(DicomTag.BitsAllocated, 8)
  79. .Add<ushort>(DicomTag.BitsStored, 8)
  80. .Add<ushort>(DicomTag.HighBit, 7)
  81. .Add(DicomTag.PixelRepresentation, (ushort)PixelRepresentation.Unsigned)
  82. .Add(DicomTag.PlanarConfiguration, (ushort)PlanarConfiguration.Interleaved)
  83. .Add<ushort>(DicomTag.SamplesPerPixel, 1)
  84. .Add(DicomTag.PhotometricInterpretation, PhotometricInterpretation.Monochrome2.Value);
  85. var pixelData = DicomPixelData.Create(dataset, true);
  86. var pixels = GetGreyBytes(bitmap);
  87. var buffer = new Dicom.IO.Buffer.MemoryByteBuffer(pixels.Data);
  88. pixelData.AddFrame(buffer);
  89. var imageBox = _currentFilmBox.BasicImageBoxes[index];
  90. imageBox.ImageSequence = dataset;
  91. pixels.Dispose();
  92. }
  93. private void AddColorImage(Bitmap bitmap, int index)
  94. {
  95. if (_currentFilmBox == null)
  96. {
  97. throw new InvalidOperationException("Start film box first!");
  98. }
  99. if (index < 0 || index > _currentFilmBox.BasicImageBoxes.Count)
  100. {
  101. throw new ArgumentOutOfRangeException(nameof(index), "Image box index out of range");
  102. }
  103. if (bitmap.PixelFormat != PixelFormat.Format24bppRgb && bitmap.PixelFormat != PixelFormat.Format32bppArgb
  104. && bitmap.PixelFormat != PixelFormat.Format32bppRgb)
  105. {
  106. throw new ArgumentException("Not supported bitmap format", nameof(bitmap));
  107. }
  108. var dataset = new DicomDataset();
  109. dataset.Add<ushort>(DicomTag.Columns, (ushort)bitmap.Width)
  110. .Add<ushort>(DicomTag.Rows, (ushort)bitmap.Height)
  111. .Add<ushort>(DicomTag.BitsAllocated, 8)
  112. .Add<ushort>(DicomTag.BitsStored, 8)
  113. .Add<ushort>(DicomTag.HighBit, 7)
  114. .Add(DicomTag.PixelRepresentation, (ushort)PixelRepresentation.Unsigned)
  115. .Add(DicomTag.PlanarConfiguration, (ushort)PlanarConfiguration.Interleaved)
  116. .Add<ushort>(DicomTag.SamplesPerPixel, 3)
  117. .Add(DicomTag.PhotometricInterpretation, PhotometricInterpretation.Rgb.Value);
  118. var pixelData = DicomPixelData.Create(dataset, true);
  119. var pixels = GetColorbytes(bitmap);
  120. var buffer = new Dicom.IO.Buffer.MemoryByteBuffer(pixels.Data);
  121. pixelData.AddFrame(buffer);
  122. var imageBox = _currentFilmBox.BasicImageBoxes[index];
  123. imageBox.ImageSequence = dataset;
  124. pixels.Dispose();
  125. }
  126. public void EndFilmBox()
  127. {
  128. _currentFilmBox = null;
  129. }
  130. public async Task Print()
  131. {
  132. var dicomClient = new DicomClient(RemoteAddress, RemotePort, false, CallingAE, CalledAE);
  133. await dicomClient.AddRequestAsync(
  134. new DicomNCreateRequest(FilmSession.SOPClassUID, FilmSession.SOPInstanceUID)
  135. {
  136. Dataset = FilmSession
  137. });
  138. foreach (var filmbox in FilmSession.BasicFilmBoxes)
  139. {
  140. var imageBoxRequests = new List<DicomNSetRequest>();
  141. var filmBoxRequest = new DicomNCreateRequest(FilmBox.SOPClassUID, filmbox.SOPInstanceUID)
  142. {
  143. Dataset = filmbox
  144. };
  145. filmBoxRequest.OnResponseReceived = (request, response) =>
  146. {
  147. if (response.HasDataset)
  148. {
  149. var seq = response.Dataset.GetSequence(DicomTag.ReferencedImageBoxSequence);
  150. for (int i = 0; i < seq.Items.Count; i++)
  151. {
  152. var req = imageBoxRequests[i];
  153. var imageBox = req.Dataset;
  154. var sopInstanceUid = seq.Items[i].GetSingleValue<string>(DicomTag.ReferencedSOPInstanceUID);
  155. imageBox.AddOrUpdate(DicomTag.SOPInstanceUID, sopInstanceUid);
  156. req.Command.AddOrUpdate(DicomTag.RequestedSOPInstanceUID, sopInstanceUid);
  157. }
  158. }
  159. };
  160. await dicomClient.AddRequestAsync(filmBoxRequest);
  161. foreach (var image in filmbox.BasicImageBoxes)
  162. {
  163. var req = new DicomNSetRequest(image.SOPClassUID, image.SOPInstanceUID) { Dataset = image };
  164. imageBoxRequests.Add(req);
  165. await dicomClient.AddRequestAsync(req);
  166. }
  167. }
  168. await dicomClient.AddRequestAsync(new DicomNActionRequest(FilmSession.SOPClassUID, FilmSession.SOPInstanceUID, 0x0001));
  169. await dicomClient.SendAsync();
  170. }
  171. private unsafe PinnedByteArray GetGreyBytes(Bitmap bitmap)
  172. {
  173. var pixels = new PinnedByteArray(bitmap.Width * bitmap.Height);
  174. var data = bitmap.LockBits(
  175. new Rectangle(0, 0, bitmap.Width, bitmap.Height),
  176. ImageLockMode.ReadOnly,
  177. bitmap.PixelFormat);
  178. var srcComponents = bitmap.PixelFormat == PixelFormat.Format24bppRgb ? 3 : 4;
  179. var dstLine = (byte*)pixels.Pointer;
  180. var srcLine = (byte*)data.Scan0.ToPointer();
  181. for (int i = 0; i < data.Height; i++)
  182. {
  183. for (int j = 0; j < data.Width; j++)
  184. {
  185. var pixel = srcLine + j * srcComponents;
  186. int grey = (int)(pixel[0] * 0.3 + pixel[1] * 0.59 + pixel[2] * 0.11);
  187. dstLine[j] = (byte)grey;
  188. }
  189. srcLine += data.Stride;
  190. dstLine += data.Width;
  191. }
  192. bitmap.UnlockBits(data);
  193. return pixels;
  194. }
  195. private unsafe PinnedByteArray GetColorbytes(Bitmap bitmap)
  196. {
  197. var pixels = new PinnedByteArray(bitmap.Width * bitmap.Height * 3);
  198. var data = bitmap.LockBits(
  199. new Rectangle(0, 0, bitmap.Width, bitmap.Height),
  200. ImageLockMode.ReadOnly,
  201. bitmap.PixelFormat);
  202. var srcComponents = bitmap.PixelFormat == PixelFormat.Format24bppRgb ? 3 : 4;
  203. var dstLine = (byte*)pixels.Pointer;
  204. var srcLine = (byte*)data.Scan0.ToPointer();
  205. for (int i = 0; i < data.Height; i++)
  206. {
  207. for (int j = 0; j < data.Width; j++)
  208. {
  209. var srcPixel = srcLine + j * srcComponents;
  210. var dstPixel = dstLine + j * 3;
  211. //convert from bgr to rgb
  212. dstPixel[0] = srcPixel[2];
  213. dstPixel[1] = srcPixel[1];
  214. dstPixel[2] = srcPixel[0];
  215. }
  216. srcLine += data.Stride;
  217. dstLine += data.Width * 3;
  218. }
  219. bitmap.UnlockBits(data);
  220. return pixels;
  221. }
  222. }
  223. }