Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #14 from slicebox/release-0-1-1
Moved Dicom parts to DicomParts, added some utils for attributes
- Loading branch information
Showing
11 changed files
with
186 additions
and
67 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
106 changes: 106 additions & 0 deletions
106
src/main/scala/se/nimsa/dcm4che/streams/DicomParts.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
/* | ||
* Copyright 2017 Lars Edenbrandt | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package se.nimsa.dcm4che.streams | ||
|
||
import akka.util.ByteString | ||
import org.dcm4che3.data.{SpecificCharacterSet, VR} | ||
|
||
|
||
object DicomParts { | ||
|
||
trait DicomPart { | ||
def bigEndian: Boolean | ||
|
||
def bytes: ByteString | ||
} | ||
|
||
case class DicomPreamble(bytes: ByteString) extends DicomPart { | ||
def bigEndian = false | ||
} | ||
|
||
case class DicomHeader(tag: Int, vr: VR, length: Int, isFmi: Boolean, bigEndian: Boolean, explicitVR: Boolean, bytes: ByteString) extends DicomPart { | ||
|
||
def withUpdatedLength(newLength: Int) : DicomHeader = { | ||
|
||
val updated = if ((bytes.size >= 8) && (explicitVR) && (vr.headerLength == 8)) { //explicit vr | ||
bytes.take(6) ++ DicomParsing.shortToBytes(newLength.toShort, bigEndian) | ||
} else if ((bytes.size >= 12) && (explicitVR) && (vr.headerLength == 12)) { //explicit vr | ||
bytes.take(8) ++ DicomParsing.intToBytes(newLength, bigEndian) | ||
} else { //implicit vr | ||
bytes.take(4) ++ DicomParsing.intToBytes(newLength, bigEndian) | ||
} | ||
|
||
DicomHeader(tag, vr, newLength, isFmi, bigEndian, explicitVR, updated) | ||
} | ||
|
||
} | ||
|
||
case class DicomValueChunk(bigEndian: Boolean, bytes: ByteString, last: Boolean) extends DicomPart | ||
|
||
case class DicomDeflatedChunk(bigEndian: Boolean, bytes: ByteString) extends DicomPart | ||
|
||
case class DicomItem(length: Int, bigEndian: Boolean, bytes: ByteString) extends DicomPart | ||
|
||
case class DicomItemDelimitation(bigEndian: Boolean, bytes: ByteString) extends DicomPart | ||
|
||
case class DicomSequence(tag: Int, bigEndian: Boolean, bytes: ByteString) extends DicomPart | ||
|
||
case class DicomSequenceDelimitation(bigEndian: Boolean, bytes: ByteString) extends DicomPart | ||
|
||
case class DicomFragments(tag: Int, vr: VR, bigEndian: Boolean, bytes: ByteString) extends DicomPart | ||
|
||
case class DicomFragmentsDelimitation(bigEndian: Boolean, bytes: ByteString) extends DicomPart | ||
|
||
case class DicomUnknownPart(bigEndian: Boolean, bytes: ByteString) extends DicomPart | ||
|
||
case class DicomFragment(bigEndian: Boolean, valueChunks: Seq[DicomValueChunk]) extends DicomPart { | ||
def bytes = valueChunks.map(_.bytes).fold(ByteString.empty)(_ ++ _) | ||
} | ||
|
||
case class DicomAttribute(header: DicomHeader, valueChunks: Seq[DicomValueChunk]) extends DicomPart { | ||
def bytes = valueChunks.map(_.bytes).fold(ByteString.empty)(_ ++ _) | ||
|
||
def bigEndian = header.bigEndian | ||
|
||
// LO: long string 64 chars max | ||
// SH: short string 16 chars max | ||
// PN: person name | ||
// UI: 64 chars max | ||
def withUpdatedStringValue(newValue: String, cs: SpecificCharacterSet = SpecificCharacterSet.ASCII): DicomAttribute = { | ||
val newBytes = header.vr.toBytes(newValue, cs) | ||
val needsPadding = newBytes.size % 2 == 1 | ||
val newLength = if (needsPadding) newBytes.size + 1 else newBytes.size | ||
val updatedHeader = header.withUpdatedLength(newLength.toShort) | ||
val updatedValue = if (needsPadding) { | ||
ByteString.fromArray(newBytes :+ header.vr.paddingByte().toByte) | ||
} else { | ||
ByteString.fromArray(newBytes) | ||
} | ||
DicomAttribute(updatedHeader, Seq(DicomValueChunk(header.bigEndian, updatedValue, true))) | ||
} | ||
|
||
// DA: A string of characters of the format YYYYMMDD, 8 bytes fixed | ||
def withUpdatedDateValue(newValue: String, cs: SpecificCharacterSet = SpecificCharacterSet.ASCII): DicomAttribute = { | ||
val newBytes = header.vr.toBytes(newValue, cs) | ||
val updatedValue = ByteString.fromArray(newBytes) | ||
DicomAttribute(header, Seq(DicomValueChunk(header.bigEndian, updatedValue, true))) | ||
} | ||
|
||
def asDicomParts: Seq[DicomPart] = header +: valueChunks | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
65 changes: 65 additions & 0 deletions
65
src/test/scala/se/nimsa/dcm4che/streams/DicomPartsTest.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
package se.nimsa.dcm4che.streams | ||
|
||
import org.scalatest.{FlatSpecLike, Matchers} | ||
import DicomData._ | ||
import akka.util.ByteString | ||
import se.nimsa.dcm4che.streams.DicomParts.{DicomAttribute, DicomHeader, DicomValueChunk} | ||
|
||
class DicomPartsTest extends FlatSpecLike with Matchers { | ||
|
||
"DicomHeader" should "should return a new header with modified length for explicitVR, LE" in { | ||
val (tag, vr, headerLength, length) = DicomParsing.readHeaderExplicitVR(patientNameJohnDoe, false).get | ||
val header = DicomHeader(tag, vr, length, false, false, true, patientNameJohnDoe.take(8)) | ||
val updatedHeader = header.withUpdatedLength(5) | ||
|
||
updatedHeader.length shouldEqual 5 | ||
updatedHeader.bytes.take(6) shouldEqual header.bytes.take(6) | ||
updatedHeader.bytes(6) shouldEqual 5 | ||
updatedHeader.bytes(7) shouldEqual 0 | ||
} | ||
|
||
it should "should return a new header with modified length for explicitVR, BE" in { | ||
val (tag, vr, headerLength, length) = DicomParsing.readHeaderExplicitVR(patientNameJohnDoeBE, true).get | ||
val header = DicomHeader(tag, vr, length, false, true, true, patientNameJohnDoeBE.take(8)) | ||
val updatedHeader = header.withUpdatedLength(5) | ||
|
||
updatedHeader.length shouldEqual 5 | ||
updatedHeader.bytes.take(6) shouldEqual header.bytes.take(6) | ||
updatedHeader.bytes(6) shouldEqual 0 | ||
updatedHeader.bytes(7) shouldEqual 5 | ||
} | ||
|
||
it should "should return a new header with modified length for implicitVR, LE" in { | ||
val (tag, vr, headerLength, length) = DicomParsing.readHeaderImplicitVR(patientNameJohnDoeImplicit).get | ||
val header = DicomHeader(tag, vr, length, false, false, false, patientNameJohnDoeImplicit.take(8)) | ||
val updatedHeader = header.withUpdatedLength(5) | ||
|
||
updatedHeader.length shouldEqual 5 | ||
updatedHeader.bytes.take(4) shouldEqual header.bytes.take(4) | ||
updatedHeader.bytes(4) shouldEqual 5 | ||
updatedHeader.bytes(5) shouldEqual 0 | ||
} | ||
|
||
|
||
"DicomAttribute" should "should return a new attribute with updated header and updated value" in { | ||
val (tag, vr, headerLength, length) = DicomParsing.readHeaderExplicitVR(patientNameJohnDoe, false).get | ||
val header = DicomHeader(tag, vr, length, false, false, true, patientNameJohnDoe.take(8)) | ||
val value = DicomValueChunk(false, patientNameJohnDoe.drop(8) ,true) | ||
val attribute = DicomAttribute(header, Seq(value)) | ||
val updatedAttribute = attribute.withUpdatedStringValue("Jimmyboy^Doe") | ||
updatedAttribute.bytes.size shouldEqual 12 | ||
updatedAttribute.header.length shouldEqual 12 | ||
} | ||
|
||
it should "should return a new attribute with updated header and updated value with padding" in { | ||
val (tag, vr, headerLength, length) = DicomParsing.readHeaderExplicitVR(patientNameJohnDoe, false).get | ||
val header = DicomHeader(tag, vr, length, false, false, true, patientNameJohnDoe.take(8)) | ||
val value = DicomValueChunk(false, patientNameJohnDoe.drop(8) ,true) | ||
val attribute = DicomAttribute(header, Seq(value)) | ||
val updatedAttribute = attribute.withUpdatedStringValue("Jimmy^Doe") | ||
|
||
updatedAttribute.bytes.size shouldEqual 10 | ||
updatedAttribute.header.length shouldEqual 10 | ||
updatedAttribute.bytes.drop(9) shouldEqual ByteString(32) | ||
} | ||
} |