Skip to content

Commit bf55131

Browse files
committed
Optimize memory usage
1 parent ef04346 commit bf55131

File tree

5 files changed

+102
-68
lines changed

5 files changed

+102
-68
lines changed

pkg/codec/codec.cxx

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -173,13 +173,12 @@ unsigned char * J2K_Decode(unsigned char * dataSrc, size_t length, int width, in
173173
void *vOutBits = NULL;
174174
OPJ_INT32 * ptr;
175175
int adjustR, bitsAllocated;
176+
adjustR = 0;
176177
bitsAllocated = image->comps[0].prec;
177178
if (bitsAllocated == 8) {
178179
BYTE v;
179180
BYTE *newdata = new BYTE[image->x1*image->y1*image->numcomps];
180181
for (unsigned int c=0; c<image->numcomps; c++) {
181-
adjustR =
182-
(image->comps[c].sgnd ? 1 << (bitsAllocated - 1) : 0);
183182
ptr = (OPJ_INT32 *)image->comps[c].data;
184183
for (unsigned int i = 0; i< image->x1*image->y1; i++){
185184
v = (BYTE) (*ptr + adjustR);
@@ -192,8 +191,6 @@ unsigned char * J2K_Decode(unsigned char * dataSrc, size_t length, int width, in
192191
OPJ_UINT16 v;
193192
OPJ_UINT16 *newdata = new OPJ_UINT16[image->x1*image->y1*image->numcomps];
194193
for (unsigned int c=0; c<image->numcomps; c++) {
195-
adjustR =
196-
(image->comps[c].sgnd ? 1 << (bitsAllocated - 1) : 0);
197194
ptr = (OPJ_INT32 *)image->comps[c].data;
198195
for (unsigned int i = 0; i< image->x1*image->y1; i++){
199196
v = (OPJ_UINT16) (*ptr + adjustR);

pkg/codec/decoder.go

Lines changed: 92 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -56,87 +56,121 @@ func GetNativePixelData(e frame.EncapsulatedFrame) ([]byte, error) {
5656

5757
// GetStdImage returns the converted image in Go standard format
5858
func GetStdImage(e frame.EncapsulatedFrame) (image.Image, error) {
59-
buf := bytes.NewBuffer(e.Data)
60-
r := dicomio.NewReader(bufio.NewReader(buf), binary.LittleEndian, int64(len(e.Data)))
6159
var (
62-
upLeft = image.Point{0, 0}
63-
lowRight = image.Point{e.Cols, e.Rows}
64-
img8 = image.NewRGBA(image.Rectangle{upLeft, lowRight})
65-
img16 = image.NewRGBA64(image.Rectangle{upLeft, lowRight})
66-
samplesPerPixel int
67-
values8 [3]uint8 // red, green, blue
68-
values16 [3]uint16
69-
cols = e.Cols
70-
a = 0xffff
71-
i = 0
60+
upLeft = image.Point{0, 0}
61+
lowRight = image.Point{e.Cols, e.Rows}
62+
img8 = image.NewRGBA(image.Rectangle{upLeft, lowRight})
63+
img16 = image.NewRGBA64(image.Rectangle{upLeft, lowRight})
64+
cols = e.Cols
65+
a = 0xffff
66+
pixelValues = make([]int, e.SamplesPerPixel)
7267
)
73-
if e.BitsAllocated == 8 {
74-
for !r.IsLimitExhausted() {
75-
i++
76-
v, err := r.ReadUInt8()
77-
if err != nil {
78-
return nil, fmt.Errorf("read uint8 failed: %w", err)
68+
decodedBytes, err := GetNativePixelData(e)
69+
if err != nil {
70+
return nil, fmt.Errorf("decode pixeldata: %w", err)
71+
}
72+
buf := bytes.NewBuffer(decodedBytes)
73+
r := dicomio.NewReader(bufio.NewReader(buf), binary.LittleEndian, int64(buf.Len()))
74+
oneByte := e.BitsAllocated == 8
75+
for i := 0; i < e.Cols*e.Rows; i++ {
76+
var clr color.Color
77+
err := readPixelValues(e, *r, pixelValues)
78+
if err != nil {
79+
return nil, fmt.Errorf("read pixel values: %w", err)
80+
}
81+
switch e.SamplesPerPixel {
82+
case 1:
83+
r := pixelValues[0]
84+
if oneByte {
85+
clr = color.Gray{Y: uint8(r)}
86+
} else {
87+
clr = color.Gray16{Y: uint16(r)}
7988
}
80-
values8[samplesPerPixel%3] = v
81-
samplesPerPixel++
82-
if samplesPerPixel < e.SamplesPerPixel {
83-
continue
89+
case 3:
90+
r, g, b := pixelValues[0], pixelValues[1], pixelValues[2]
91+
if oneByte {
92+
clr = color.RGBA{uint8(r), uint8(g), uint8(b), uint8(a)}
93+
} else {
94+
clr = color.RGBA64{uint16(r), uint16(g), uint16(b), uint16(a)}
8495
}
85-
var clr color.Color
86-
switch samplesPerPixel {
87-
case 1:
88-
clr = color.Gray{Y: values8[0]}
89-
case 3:
90-
r, g, b := values8[0], values8[1], values8[2]
91-
clr = color.RGBA{r, g, b, uint8(a)}
96+
case 4:
97+
r, g, b, a := pixelValues[0], pixelValues[1], pixelValues[2], pixelValues[3]
98+
if oneByte {
99+
clr = color.RGBA{uint8(r), uint8(g), uint8(b), uint8(a)}
100+
} else {
101+
clr = color.RGBA64{uint16(r), uint16(g), uint16(b), uint16(a)}
92102
}
103+
}
104+
if oneByte {
93105
img8.Set(i%cols, i/cols, clr)
94-
samplesPerPixel = 0
106+
} else {
107+
img16.Set(i%cols, i/cols, clr)
95108
}
109+
}
110+
if oneByte {
96111
return img8, nil
97112
}
98-
for !r.IsLimitExhausted() {
99-
i++
100-
v, err := r.ReadUInt16()
101-
if err != nil {
102-
return nil, fmt.Errorf("read uint16 failed: %w", err)
103-
}
104-
values16[samplesPerPixel%3] = v
105-
samplesPerPixel++
106-
if samplesPerPixel < e.SamplesPerPixel {
107-
continue
113+
return img16, nil
114+
}
115+
116+
func readPixelValues(e frame.EncapsulatedFrame, r dicomio.Reader, pixelValues []int) error {
117+
for sample := 0; sample < e.SamplesPerPixel; sample++ {
118+
if r.IsLimitExhausted() {
119+
break
108120
}
109-
var clr color.Color
110-
switch samplesPerPixel {
111-
case 1:
112-
clr = color.Gray16{Y: values16[0]}
113-
case 3:
114-
r, g, b := values16[0], values16[1], values16[2]
115-
clr = color.RGBA64{r, g, b, uint16(a)}
121+
switch e.BitsAllocated {
122+
case 8:
123+
v, err := r.ReadUInt8()
124+
if err != nil {
125+
return fmt.Errorf("read uint8 failed: %w", err)
126+
}
127+
pixelValues[sample] = int(v)
128+
case 16:
129+
v, err := r.ReadUInt16()
130+
if err != nil {
131+
return fmt.Errorf("read uint16 failed: %w", err)
132+
}
133+
pixelValues[sample] = int(v)
134+
case 32:
135+
v, err := r.ReadUInt32()
136+
if err != nil {
137+
return fmt.Errorf("read uint32 failed: %w", err)
138+
}
139+
pixelValues[sample] = int(v)
140+
default:
141+
return fmt.Errorf("bitsAllocated not supported: %d", e.BitsAllocated)
116142
}
117-
img16.Set(i%cols, i/cols, clr)
118-
samplesPerPixel = 0
119143
}
120-
return img16, nil
144+
return nil
121145
}
122146

123147
// Decode reads a JPEG image from EncapsulatedFrame and returns it as an NativeFrame.
124148
func Decode(e frame.EncapsulatedFrame) (frame.NativeFrame, error) {
125-
img, err := GetStdImage(e)
149+
var nativeData = make([][]int, e.Cols*e.Rows)
150+
decodedBytes, err := GetNativePixelData(e)
126151
if err != nil {
127-
return frame.NativeFrame{}, fmt.Errorf("get Go's std image: %w", err)
152+
return frame.NativeFrame{}, fmt.Errorf("decode pixeldata: %w", err)
128153
}
129-
var nativeData [][]int
154+
buf := bytes.NewBuffer(decodedBytes)
155+
r := dicomio.NewReader(bufio.NewReader(buf), binary.LittleEndian, int64(buf.Len()))
156+
var (
157+
pixelValues = make([]int, e.SamplesPerPixel)
158+
)
130159
for i := 0; i < e.Cols*e.Rows; i++ {
131-
clr := img.At(i%e.Cols, i/e.Cols)
132-
r, g, b, a := clr.RGBA()
160+
err := readPixelValues(e, *r, pixelValues)
161+
if err != nil {
162+
return frame.NativeFrame{}, fmt.Errorf("read pixel values: %w", err)
163+
}
133164
switch e.SamplesPerPixel {
134165
case 1:
135-
nativeData = append(nativeData, []int{int(r)})
166+
r := pixelValues[0]
167+
nativeData[i] = []int{int(r)}
136168
case 3:
137-
nativeData = append(nativeData, []int{int(r), int(g), int(b)})
169+
r, g, b := pixelValues[0], pixelValues[1], pixelValues[2]
170+
nativeData[i] = []int{int(r), int(g), int(b)}
138171
case 4:
139-
nativeData = append(nativeData, []int{int(r), int(g), int(b), int(a)})
172+
r, g, b, a := pixelValues[0], pixelValues[1], pixelValues[2], pixelValues[3]
173+
nativeData[i] = []int{int(r), int(g), int(b), int(a)}
140174
}
141175
}
142176
nf := frame.NativeFrame{

pkg/codec/decoder_test.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,6 @@ func assertJPEGDecompressing(t *testing.T, tc testify) {
105105
t.Fatal("failed to decode image")
106106
}
107107

108-
fr.Data = uncompressedBytes
109108
img, err := GetStdImage(fr)
110109
if err != nil {
111110
t.Fatal(err)

read.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ func (r *reader) readHeader() ([]*Element, error) {
239239
return metaElems, nil
240240
}
241241

242-
func newEncapsulatedFrame(d *Dataset, data []byte) (*frame.Frame, error) {
242+
func NewEncapsulatedFrame(d *Dataset, data []byte) (*frame.Frame, error) {
243243
e := frame.EncapsulatedFrame{
244244
Data: data,
245245
}
@@ -312,7 +312,7 @@ func (r *reader) readPixelData(vl uint32, d *Dataset, fc chan<- *frame.Frame) (V
312312
break
313313
}
314314

315-
f, err := newEncapsulatedFrame(d, data)
315+
f, err := NewEncapsulatedFrame(d, data)
316316
if err != nil {
317317
break
318318
}
@@ -410,7 +410,7 @@ func makeErrorPixelData(d *Dataset, reader io.Reader, vl uint32, fc chan<- *fram
410410
return nil, fmt.Errorf("makeErrorPixelData: read pixelData: %w", err)
411411
}
412412

413-
f, err := newEncapsulatedFrame(d, data)
413+
f, err := NewEncapsulatedFrame(d, data)
414414
if err != nil {
415415
return nil, err
416416
}

write_test.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -411,8 +411,12 @@ func TestWrite(t *testing.T) {
411411
IsEncapsulated: true,
412412
Frames: []*frame.Frame{
413413
{
414-
Encapsulated: true,
415-
EncapsulatedData: frame.EncapsulatedFrame{Data: []byte{1, 2, 3, 4}},
414+
Encapsulated: true,
415+
EncapsulatedData: frame.EncapsulatedFrame{
416+
Data: []byte{1, 2, 3, 4},
417+
TransferSyntax: uid.ExplicitVRLittleEndian,
418+
BitsAllocated: 8,
419+
},
416420
},
417421
},
418422
}),

0 commit comments

Comments
 (0)