-
Notifications
You must be signed in to change notification settings - Fork 1
/
io.go
211 lines (188 loc) · 7.04 KB
/
io.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
package gonii
import (
"bytes"
"encoding/binary"
"net/http"
"os"
"github.com/okieraised/gonii/internal/utils"
"github.com/okieraised/gonii/pkg/nifti"
)
//----------------------------------------------------------------------------------------------------------------------
// Define Reader methods
//----------------------------------------------------------------------------------------------------------------------
// NewNiiReader returns a new NIfTI reader
//
// Options:
// - `WithReadInMemory(inMemory bool)` : Read the whole file into memory
// - `WithReadRetainHeader(retainHeader bool)` : Whether to retain the header structure after parsing
// - `WithReadHeaderFile(headerFile string)` : Specify a header file path in case of separate .hdr/.img file
// - `WithReadImageFile(niiFile string)` : Specify an image file path
// - `WithReadImageReader(r *bytes.Reader)` : Specify a header file reader in case of separate .hdr/.img file
// - `WithReadHeaderReader(r *bytes.Reader)` : Specify an image file reader
func NewNiiReader(options ...func(*nifti.NiiReader) error) (nifti.Reader, error) {
// Init new reader
reader := new(nifti.NiiReader)
reader.SetBinaryOrder(binary.LittleEndian)
reader.SetDataset(&nifti.Nii{})
for _, opt := range options {
err := opt(reader)
if err != nil {
return nil, err
}
}
return reader, nil
}
// WithReadInMemory allows option to read the whole file into memory. The default is true.
// This is for future implementation. Currently, all file is read into memory before parsing
func WithReadInMemory(inMemory bool) func(*nifti.NiiReader) error {
return func(w *nifti.NiiReader) error {
w.SetInMemory(inMemory)
return nil
}
}
// WithReadRetainHeader allows option to keep the header after parsing instead of just keeping the NIfTI data structure
func WithReadRetainHeader(retainHeader bool) func(*nifti.NiiReader) error {
return func(w *nifti.NiiReader) error {
w.SetRetainHeader(retainHeader)
return nil
}
}
// WithReadHeaderFile allows option to specify the separate header file in case of NIfTI pair .hdr/.img
func WithReadHeaderFile(headerFile string) func(*nifti.NiiReader) error {
return func(w *nifti.NiiReader) error {
bData, err := os.ReadFile(headerFile)
if err != nil {
return err
}
// Check the content type to see if the file is gzipped. Do not depend on just the extensions of the file
bData, err = deflateFileContent(bData)
if err != nil {
return err
}
w.SetHdrReader(bytes.NewReader(bData))
return nil
}
}
// WithReadImageFile allows option to specify the NIfTI file (.nii.gz or .nii)
func WithReadImageFile(niiFile string) func(*nifti.NiiReader) error {
return func(w *nifti.NiiReader) error {
bData, err := os.ReadFile(niiFile)
if err != nil {
return err
}
// Check the content type to see if the file is gzipped. Do not depend on just the extensions of the file
bData, err = deflateFileContent(bData)
if err != nil {
return err
}
w.SetReader(bytes.NewReader(bData))
return nil
}
}
// WithReadImageReader allows option for users to specify the NIfTI bytes reader (.nii.gz or .nii)
func WithReadImageReader(r *bytes.Reader) func(*nifti.NiiReader) error {
return func(w *nifti.NiiReader) error {
bArr := make([]byte, r.Len())
_, err := r.Read(bArr)
if err != nil {
return err
}
bArr, err = deflateFileContent(bArr)
if err != nil {
return err
}
w.SetReader(bytes.NewReader(bArr))
return nil
}
}
// WithReadHeaderReader allows option for users to specify the separate header file reader in case of NIfTI pair .hdr/.img
func WithReadHeaderReader(r *bytes.Reader) func(*nifti.NiiReader) error {
return func(w *nifti.NiiReader) error {
bArr := make([]byte, r.Len())
_, err := r.Read(bArr)
if err != nil {
return err
}
bArr, err = deflateFileContent(bArr)
if err != nil {
return err
}
w.SetHdrReader(r)
return nil
}
}
//----------------------------------------------------------------------------------------------------------------------
// Define Writer methods
//----------------------------------------------------------------------------------------------------------------------
// NewNiiWriter returns a new NIfTI writer. If no version is specified, the writer will default to write to NIfTI version 1
func NewNiiWriter(filePath string, options ...func(*nifti.NiiWriter)) (nifti.Writer, error) {
writer := new(nifti.NiiWriter)
writer.SetFilePath(filePath)
writer.SetWriteHeaderFile(false) // Default to false. Write to a single file only
writer.SetCompression(false) // Default to false. No compression
writer.SetVersion(nifti.NIIVersion1) // Default to version 1
// Other options
for _, opt := range options {
opt(writer)
}
return writer, nil
}
// WithWriteHeaderFile sets the option to write NIfTI image to a header/image (.hdr/.img) file pair
//
// If true, output will be two files for the header and the image. Default is false.
func WithWriteHeaderFile(writeHeaderFile bool) func(*nifti.NiiWriter) {
return func(w *nifti.NiiWriter) {
w.SetWriteHeaderFile(writeHeaderFile)
}
}
// WithWriteCompression sets the option to write compressed NIfTI image to a single file (.nii.gz)
//
// If true, the whole file will be compressed. Default is false.
func WithWriteCompression(withCompression bool) func(writer *nifti.NiiWriter) {
return func(w *nifti.NiiWriter) {
w.SetCompression(withCompression)
}
}
// WithWriteNii1Header sets the option to allow user to provide predefined NIfTI-1 header structure.
//
// If no header provided, the header will be converted from the NIfTI image structure
func WithWriteNii1Header(header *nifti.Nii1Header) func(*nifti.NiiWriter) {
return func(w *nifti.NiiWriter) {
w.SetHeader(header)
}
}
// WithWriteNii2Header sets the option to allow user to provide predefined NIfTI-2 header structure.
//
// If no header provided, the header will be converted from the NIfTI image structure
func WithWriteNii2Header(header *nifti.Nii2Header) func(*nifti.NiiWriter) {
return func(w *nifti.NiiWriter) {
w.SetHeader(header)
}
}
// WithWriteNIfTIData sets the option to allow user to provide predefined NIfTI-1 data structure.
func WithWriteNIfTIData(data *nifti.Nii) func(writer *nifti.NiiWriter) {
return func(w *nifti.NiiWriter) {
w.SetNiiData(data)
}
}
// WithWriteVersion sets the option to specify the exported NIfTI version (NIfTI-1 or 2). Default is NIfTI-1
func WithWriteVersion(version int) func(writer *nifti.NiiWriter) {
return func(w *nifti.NiiWriter) {
w.SetVersion(version)
}
}
//----------------------------------------------------------------------------------------------------------------------
// Define Support function
//----------------------------------------------------------------------------------------------------------------------
// deflateFileContent deflates the gzipped binary to its original content
func deflateFileContent(bData []byte) ([]byte, error) {
var err error
mimeType := http.DetectContentType(bData[:512])
if mimeType == "application/x-gzip" {
bData, err = utils.DeflateGzip(bData)
if err != nil {
return nil, err
}
}
return bData, nil
}