Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 26 additions & 13 deletions read.go
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,9 @@ func (r *reader) readNativeFrames(parsedData *Dataset, fc chan<- *frame.Frame, v
bytesToRead = pixelsPerFrame * samplesPerPixel / 8 * nFrames
}

// Used to read whole frames at a time in readNativeFrame.
bytesPerFrame := bytesAllocated * samplesPerPixel * pixelsPerFrame

skipFinalPaddingByte := false
if uint32(bytesToRead) != vl {
switch {
Expand All @@ -487,7 +490,9 @@ func (r *reader) readNativeFrames(parsedData *Dataset, fc chan<- *frame.Frame, v
}
image.Frames = make([]*frame.Frame, nFrames)
bo := r.rawReader.ByteOrder()
pixelBuf := make([]byte, bytesAllocated)
// Allocating a buffer to hold a whole frame allows us to read the frame in one large chunk
// which significantly improves performance compared to reading pixel by pixel.
frameBuf := make([]byte, bytesPerFrame)
for frameIdx := 0; frameIdx < nFrames; frameIdx++ {
// Init current frame
currentFrame := frame.Frame{
Expand All @@ -509,11 +514,11 @@ func (r *reader) readNativeFrames(parsedData *Dataset, fc chan<- *frame.Frame, v
} else {
switch bitsAllocated {
case 8:
currentFrame, _, err = readNativeFrame[uint8](bitsAllocated, MustGetInts(rows.Value)[0], MustGetInts(cols.Value)[0], bytesToRead, samplesPerPixel, pixelsPerFrame, pixelBuf, r.rawReader)
currentFrame, _, err = readNativeFrame[uint8](bitsAllocated, MustGetInts(rows.Value)[0], MustGetInts(cols.Value)[0], bytesToRead, samplesPerPixel, pixelsPerFrame, frameBuf, r.rawReader)
case 16:
currentFrame, _, err = readNativeFrame[uint16](bitsAllocated, MustGetInts(rows.Value)[0], MustGetInts(cols.Value)[0], bytesToRead, samplesPerPixel, pixelsPerFrame, pixelBuf, r.rawReader)
currentFrame, _, err = readNativeFrame[uint16](bitsAllocated, MustGetInts(rows.Value)[0], MustGetInts(cols.Value)[0], bytesToRead, samplesPerPixel, pixelsPerFrame, frameBuf, r.rawReader)
case 32:
currentFrame, _, err = readNativeFrame[uint32](bitsAllocated, MustGetInts(rows.Value)[0], MustGetInts(cols.Value)[0], bytesToRead, samplesPerPixel, pixelsPerFrame, pixelBuf, r.rawReader)
currentFrame, _, err = readNativeFrame[uint32](bitsAllocated, MustGetInts(rows.Value)[0], MustGetInts(cols.Value)[0], bytesToRead, samplesPerPixel, pixelsPerFrame, frameBuf, r.rawReader)
default:
return nil, bytesToRead, fmt.Errorf("unsupported bitsAllocated, got: %v, %w", bitsAllocated, ErrorUnsupportedBitsAllocated)
}
Expand All @@ -538,40 +543,48 @@ func (r *reader) readNativeFrames(parsedData *Dataset, fc chan<- *frame.Frame, v

// readNativeFrame builds and reads a single NativeFrame[I] from the rawReader.
// TODO(suyashkumar): refactor args to an options struct, or something more compact and readable.
func readNativeFrame[I constraints.Integer](bitsAllocated, rows, cols, bytesToRead, samplesPerPixel, pixelsPerFrame int, pixelBuf []byte, rawReader *dicomio.Reader) (frame.Frame, int, error) {
func readNativeFrame[I constraints.Integer](bitsAllocated, rows, cols, bytesToRead, samplesPerPixel, pixelsPerFrame int, buf []byte, rawReader *dicomio.Reader) (frame.Frame, int, error) {
nativeFrame := frame.NewNativeFrame[I](bitsAllocated, rows, cols, pixelsPerFrame, samplesPerPixel)
currentFrame := frame.Frame{
Encapsulated: false,
NativeData: nativeFrame,
}

// Read the whole frame at once
_, err := io.ReadFull(rawReader, buf)
if err != nil {
return frame.Frame{}, bytesToRead,
fmt.Errorf("could not read uint%d from input: %w", bitsAllocated, err)
}

bo := rawReader.ByteOrder()
// bytesAllocated is not needed as we use constants in switch
bufIdx := 0

for pixel := 0; pixel < pixelsPerFrame; pixel++ {
for value := 0; value < samplesPerPixel; value++ {
_, err := io.ReadFull(rawReader, pixelBuf)
if err != nil {
return frame.Frame{}, bytesToRead,
fmt.Errorf("could not read uint%d from input: %w", bitsAllocated, err)
}
switch bitsAllocated {
case 8:
v, ok := any(pixelBuf[0]).(I)
v, ok := any(buf[bufIdx]).(I)
if !ok {
return frame.Frame{}, bytesToRead, fmt.Errorf("internal error - readNativeFrame unexpectedly unable to type cast pixel buffer data to the I type (%T), where bitsAllocated=%v", *new(I), bitsAllocated)
}
nativeFrame.RawData[(pixel*samplesPerPixel)+value] = v
bufIdx++
case 16:
v, ok := any(bo.Uint16(pixelBuf)).(I)
v, ok := any(bo.Uint16(buf[bufIdx:])).(I)
if !ok {
return frame.Frame{}, bytesToRead, fmt.Errorf("internal error - readNativeFrame unexpectedly unable to type cast pixel buffer data to the I type (%T), where bitsAllocated=%v", *new(I), bitsAllocated)
}
nativeFrame.RawData[(pixel*samplesPerPixel)+value] = v
bufIdx += 2
case 32:
v, ok := any(bo.Uint32(pixelBuf)).(I)
v, ok := any(bo.Uint32(buf[bufIdx:])).(I)
if !ok {
return frame.Frame{}, bytesToRead, fmt.Errorf("internal error - readNativeFrame unexpectedly unable to type cast pixel buffer data to the I type (%T), where bitsAllocated=%v", *new(I), bitsAllocated)
}
nativeFrame.RawData[(pixel*samplesPerPixel)+value] = v
bufIdx += 4
default:
return frame.Frame{}, bytesToRead, fmt.Errorf("readNativeFrame unsupported bitsAllocated=%d : %w", bitsAllocated, ErrorUnsupportedBitsAllocated)
}
Expand Down