• Dicom standard introduction
  • Dicom application scenarios
  • The library used by Dicom
  • OC C++ mixed implementation of Dicom

Dicom standard

Modern medical imaging is a subject based on the integration of medical imaging, transmission, storage and display technologies. All medical images reconstructed by imaging equipment or post-processing software should meet the relevant international standards to meet the requirements of modern medical imaging. DICOM is one of the two major research organizations of the American College of Radiology (ACR) and the National Electrical Manufacturers Association, NEMA is an internationally recognized and universal standard for medical image storage and transmission. DICOM is mainly involved in the storage and communication of medical images, which is the most important and difficult information system. It can solve the problems of the storage and transmission of medical images in the complex network environment of different locations, different equipment manufacturers and different countries. It can be directly applied to radiology Information System. RIS) and Picture Archiving and Communication Systems (PACS). With the wide application of PACS system in China, the requirement of medical image file format is more and more focused on the DICOM standard compliance. Only medical image data conforming to DICOM format can ensure transmission and storage between devices, servers and workstations of different manufacturers. However, the correct archiving and transmission of medical images in the full Chinese environment still cannot be tested and verified. Therefore, in order to ensure the effective exchange, analysis and sharing of medical digital image communication information in Chinese, the conformity test of medical digital image communication (DICOM) Chinese standard is very necessary.

Dicom application scenarios

This standard specifies the test method for conformity of Chinese standards for medical digital imaging equipment and PACS systems. This standard applies to the software development of various types of medical and health institutions, medical device manufacturers, medical image storage and archiving systems (PACS) manufacturers and radiographic information systems (RIS) manufacturers at all levels of the country.

Digital browsing of hospital images view via PC (2D 3D)

DCMTK(C++)

DCMTK IS a set of software libraries and applications that are part of the DICOM/MEDICOM standard.

DCMTK contains subpackages, each in its own subdirectory:

  • Config-dcmtk configuration utility
  • Dcmdata – Data encoding/decoding library and utility
  • Dcmect – library for enhanced CT objects
  • DCMFG – Library for function groups
  • **dcmimage-** Adds support for color images to dCMimgle
  • **dcmimgle-** Image processing library and utilities
  • Dcmiod – A library for processing information objects and modules
  • Dcmjpeg – Compression/decompression library and utility
  • DCMJPLS – Compression/decompression libraries and utilities
  • Dcmnet – Network library and utility applications
  • Dcmpmap – a library for handling parameterized map objects
  • **dcmpstat-** Demo state library and utility application
  • DCMQRDB – Image database server
  • DCMRT – Radiotherapy library and utilities
  • Dcmseg – library for processing subdivision objects
  • Dcmsign – A digital signature library and utility application
  • DCMSR – Structured report library and utility
  • DCMTLS – Security extension to the network library
  • Dcmtract – library for processing physical examination results
  • DCMWLM – Modal worklist database server
  • Oflog – Log4CPlus based logging library
  • Ofstd – general purpose class library

Each subdirectory (except config) contains application source code (apps), library source code (libsrc), library include files (include), configuration data (etc), documents (docs), samples, and other subdirectories that support data. (data) and test procedures (tests).

OC code use

Controller call package

- (void)viewDidLoad { [super viewDidLoad]; NSString * dicomPath = [[NSBundle mainBundle] pathForResource:@"test" ofType: @"dcm"]; / / window width beds set [self decodeAndDisplay: dicomPath]; NSString * info = [dicomDecoder infoFor:PATIENT_NAME]; self.patientName.text = [NSString stringWithFormat:@"Patient: %@", info]; info = [dicomDecoder infoFor:MODALITY]; self.modality.text = [NSString stringWithFormat:@"Modality: %@", info]; info = [dicomDecoder infoFor:SERIES_DATE]; self.date.text = info; info = [NSString stringWithFormat:@"WW/WL: %d / %d", dicom2DView.winWidth, dicom2DView.winCenter]; self.windowInfo.text = info; panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanGesture:)]; panGesture.maximumNumberOfTouches = 1; [dicom2DView addGestureRecognizer:panGesture]; [panGesture release]; }Copy the code

The decodeAndDisplay method is as follows (window width and bed setting)

- (void) displayWith:(NSInteger)windowWidth windowCenter:(NSInteger)windowCenter { if (! dicomDecoder.dicomFound || ! dicomDecoder.dicomFileReadSuccess) { [dicomDecoder release]; dicomDecoder = nil; return; } NSInteger winWidth = windowWidth; NSInteger winCenter = windowCenter; NSInteger imageWidth = dicomDecoder.width; NSInteger imageHeight = dicomDecoder.height; NSInteger bitDepth = dicomDecoder.bitDepth; NSInteger samplesPerPixel = dicomDecoder.samplesPerPixel; BOOL signedImage = dicomDecoder.signedImage; BOOL needsDisplay = NO; if (samplesPerPixel == 1 && bitDepth == 8) { Byte * pixels8 = [dicomDecoder getPixels8]; if (winWidth == 0 && winCenter == 0) { Byte max = 0, min = 255; NSInteger num = imageWidth * imageHeight; for (NSInteger i = 0; i < num; i++) { if (pixels8[i] > max) { max = pixels8[i]; } if (pixels8[i] < min) { min = pixels8[i]; }} winWidth = (NSInteger)((Max + min)/2.0 + 0.5); WinCenter = (NSInteger)((max-min)/2.0 + 0.5); } [dicom2DView setPixels8:pixels8 width:imageWidth height:imageHeight windowWidth:winWidth windowCenter:winCenter samplesPerPixel:samplesPerPixel resetScroll:YES]; needsDisplay = YES; } if (samplesPerPixel == 1 && bitDepth == 16) { ushort * pixels16 = [dicomDecoder getPixels16]; if (winWidth == 0 || winCenter == 0) { ushort max = 0, min = 65535; NSInteger num = imageWidth * imageHeight; for (NSInteger i = 0; i < num; i++) { if (pixels16[i] > max) { max = pixels16[i]; } if (pixels16[i] < min) { min = pixels16[i]; }} winWidth = (NSInteger)((Max + min)/2.0 + 0.5); WinCenter = (NSInteger)((max-min)/2.0 + 0.5); } dicom2DView.signed16Image = signedImage; [dicom2DView setPixels16:pixels16 width:imageWidth height:imageHeight windowWidth:winWidth windowCenter:winCenter samplesPerPixel:samplesPerPixel resetScroll:YES]; needsDisplay = YES; } if (samplesPerPixel == 3 && bitDepth == 8) { Byte * pixels24 = [dicomDecoder getPixels24]; if (winWidth == 0 || winCenter == 0) { Byte max = 0, min = 255; NSInteger num = imageWidth * imageHeight * 3; for (NSInteger i = 0; i < num; i++) { if (pixels24[i] > max) { max = pixels24[i]; } if (pixels24[i] < min) { min = pixels24[i]; }} winWidth = (Max + min)/2 + 0.5; WinCenter = (max-min)/2 + 0.5; } [dicom2DView setPixels8:pixels24 width:imageWidth height:imageHeight windowWidth:winWidth windowCenter:winCenter samplesPerPixel:samplesPerPixel resetScroll:YES]; needsDisplay = YES; } if (needsDisplay) { CGFloat x = (self.view.frame.size.width - imageWidth) /2; CGFloat y = (self.view.frame.size.height - imageHeight) /2; dicom2DView.frame = CGRectMake(x, y, imageWidth, imageHeight); [dicom2DView setNeedsDisplay]; NSString * info = [NSString stringWithFormat:@"WW/WL: %d / %d", dicom2DView.winWidth, dicom2DView.winCenter]; self.windowInfo.text = info; }}Copy the code

Local Dicom parsing

- (ushort) getShort { Byte b[2]; const NSInteger length = 2; NSRange range; range.location = location; range.length = length; [dicomData getBytes:b range:range]; location += length; ushort retValue = 0; if (littleEndian) retValue = (ushort)((b[1] << 8) + b[0]); else retValue = (ushort)((b[0] << 8) + b[1]); return retValue; } - (int) getInt { Byte b[4]; const NSInteger length = 4; NSRange range; range.location = location; range.length = length; [dicomData getBytes:b range:range]; location += length; int retValue = 0; if (littleEndian) retValue = ((b[3] << 24) + (b[2] << 16) + (b[1] << 8) + b[0]); else retValue = ((b[0] << 24) + (b[1] << 16) + (b[2] << 8) + b[3]); return retValue; } - (double) getDouble { Byte b[8]; const NSInteger length = 8; NSRange range; range.location = location; range.length = length; [dicomData getBytes:b range:range]; location += length; double retValue = 0; if (littleEndian) { long long high = (b[7] << 24) + (b[6] << 16) + (b[5] << 8) + b[4]; long long low = (b[3] << 24) + (b[2] << 16) + (b[1] << 8) + b[0]; retValue = (high << 32) + low; } else { long long high = (b[4] << 24) + (b[5] << 16) + (b[6] << 8) + b[7]; long long low = (b[0] << 24) + (b[1] << 16) + (b[2] << 8) + b[3]; retValue = (high << 32) + low; } return retValue; } - (float) getFloat { Byte b[4]; const NSInteger length = 4; NSRange range; range.location = location; range.length = length; [dicomData getBytes:b range:range]; location += length; int retValue = 0; if (littleEndian) retValue = ((b[3] << 24) + (b[2] << 16) + (b[1] << 8) + (int)b[0]); else retValue = (int)((b[0] << 24) + (b[1] << 16) + (b[2] << 8) + (int)b[3]); return (float)retValue; } - (BOOL) getLut:(NSInteger)length buffer:(Byte *)buf { if ((length & 1) ! = 0) { //NSString *dummy = [self getString:length]; location += length; return NO; } length = length/2; for (NSInteger i = 0; i < length; ++i) { buf[i] = (Byte)([self getShort] >> 8); } return YES; } - (NSInteger) getLength { // Get 4 bytes // Byte b[4]; const NSInteger length = 4; NSRange range; range.location = location; range.length = length; [dicomData getBytes:b range:range]; location += length; // Cannot know whether the VR is implicit or explicit without the // complete Dicom Data Dictionary. // vr = (b[0] << 8)  + b[1]; NSInteger retValue = 0; switch (vr) { case OB: case OW: case SQ: case UN: case UT: if ((b[2] == 0) || (b[3] == 0)) { // Explicit VR with 32-bit length if other two bytes are zero // retValue = [self getInt]; } else { // Implicit VR with 32-bit length // vr = IMPLICIT_VR; if (littleEndian) retValue = ((b[3] << 24) + (b[2] << 16) + (b[1] << 8) + b[0]); else retValue = ((b[0] << 24) + (b[1] << 16) + (b[2] << 8) + b[3]); } break; case AE: case AS: case AT: case CS: case DA: case DS: case DT: case FD: case FL: case IS: case LO: case LT: case PN: case SH: case SL: case SS: case ST: case TM: case UI: case UL: case US: case QQ: case RT: // Explicit vr with 16-bit length // if (littleEndian) retValue = ((b[3] << 8) + b[2]); else retValue = ((b[2] << 8) + b[3]); break; default: // Implicit VR with 32-bit length... // vr = IMPLICIT_VR; if (littleEndian) retValue = ((b[3] << 24) + (b[2] << 16) + (b[1] << 8) + b[0]); else retValue = ((b[0] << 24) + (b[1] << 16) + (b[2] << 8) + b[3]); break; } return retValue; }Copy the code