forked from enricod/golibraw
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgolibraw.go
239 lines (198 loc) · 6.37 KB
/
golibraw.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
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
// go:build (darwin && cgo) || linux
package golibraw
// #cgo LDFLAGS: -lraw
// #include <libraw/libraw.h>
import "C"
import (
"bytes"
"fmt"
"image"
"os"
"unsafe"
"github.com/lmittmann/ppm"
)
type Camera struct {
Make string
Model string
Software string
Colors uint
}
type Lens struct {
Make string
Model string
Serial string
MinFocal float64
MaxFocal float64
MaxAp4MinFocal float64
MaxAp4MaxFocal float64
}
type Metadata struct {
Timestamp int64
Width int
Height int
DataSize int64
Camera Camera
Lens Lens
ISO int
Aperture float64
Shutter float64
}
type rawImg struct {
Height int
Width int
Bits uint
DataSize int
Data []byte
}
func (r rawImg) fullBytes() []byte {
header := fmt.Sprintf("P6\n%d %d\n%d\n", r.Width, r.Height, (1<<r.Bits)-1)
return append([]byte(header), r.Data...)
}
func goResult(result C.int) error {
if int(result) == 0 {
return nil
}
p := C.libraw_strerror(result)
return fmt.Errorf("libraw error: %v", C.GoString(p))
}
func lrInit() *C.libraw_data_t {
librawProcessor := C.libraw_init(0)
return librawProcessor
}
// Reads a RAW image file from file system and exports the embedded thumbnail image - if it exists - to the path defined by exportPath parameter.
// This method is significantly faster than importing the RAW image file.
func ExtractThumbnail(inputPath string, exportPath string) error {
if _, err := os.Stat(exportPath); err == nil {
return fmt.Errorf("output file [%v] already exists", exportPath)
}
if _, err := os.Stat(inputPath); err != nil {
return fmt.Errorf("input file [%v] does not exist", exportPath)
}
librawProcessor := lrInit()
defer C.libraw_recycle(librawProcessor)
if err := goResult(C.libraw_open_file(librawProcessor, C.CString(inputPath))); err != nil {
return fmt.Errorf("failed to open input file [%v]", inputPath)
}
if err := goResult(C.libraw_unpack_thumb(librawProcessor)); err != nil {
return fmt.Errorf("unpacking thumbnail from RAW failed with [%v]", err)
}
if err := goResult(C.libraw_dcraw_thumb_writer(librawProcessor, C.CString(exportPath))); err != nil {
return fmt.Errorf("writing thumbnail failed with [%v]", err)
}
return nil
}
// Reads a RAW image file from file system and exports collected metadata.
// This method is significantly faster than importing the RAW image file.
func ExtractMetadata(path string) (Metadata, error) {
stat, err := os.Stat(path)
if err != nil {
return Metadata{}, fmt.Errorf("input file does not exist [%v]", path)
}
librawProcessor := lrInit()
defer C.libraw_recycle(librawProcessor)
if err := goResult(C.libraw_open_file(librawProcessor, C.CString(path))); err != nil {
return Metadata{}, fmt.Errorf("failed to open input file [%v]", path)
}
iparam := C.libraw_get_iparams(librawProcessor)
lensinfo := C.libraw_get_lensinfo(librawProcessor)
other := C.libraw_get_imgother(librawProcessor)
width := int(C.libraw_get_raw_width(librawProcessor))
height := int(C.libraw_get_raw_height(librawProcessor))
metadata := Metadata{
Timestamp: int64(other.timestamp),
Width: int(width),
Height: int(height),
DataSize: stat.Size(),
Camera: Camera{
Make: C.GoString(&iparam.normalized_make[0]),
Model: C.GoString(&iparam.normalized_model[0]),
Software: C.GoString(&iparam.software[0]),
Colors: uint(iparam.colors),
},
Lens: Lens{
Make: C.GoString(&lensinfo.LensMake[0]),
Model: C.GoString(&lensinfo.Lens[0]),
Serial: C.GoString(&lensinfo.LensSerial[0]),
MinFocal: float64(lensinfo.MinFocal),
MaxFocal: float64(lensinfo.MaxFocal),
MaxAp4MinFocal: float64(lensinfo.MaxAp4MinFocal),
MaxAp4MaxFocal: float64(lensinfo.MaxAp4MaxFocal),
},
ISO: int(other.iso_speed),
Aperture: float64(other.aperture),
Shutter: float64(other.shutter),
}
return metadata, nil
}
// Reads a RAW image file from file system and converts it to standard image.Image
func ImportRaw(path string) (image.Image, error) {
if _, err := os.Stat(path); err != nil {
return nil, fmt.Errorf("input file [%v] does not exist", path)
}
librawProcessor := lrInit()
defer C.libraw_recycle(librawProcessor)
err := goResult(C.libraw_open_file(librawProcessor, C.CString(path)))
if err != nil {
return nil, fmt.Errorf("failed to open file [%v]", path)
}
err = goResult(C.libraw_unpack(librawProcessor))
if err != nil {
return nil, fmt.Errorf("failed to unpack file [%v]", path)
}
err = goResult(C.libraw_dcraw_process(librawProcessor))
if err != nil {
return nil, fmt.Errorf("failed to import file [%v]", path)
}
var result C.int
img := C.libraw_dcraw_make_mem_image(librawProcessor, &result)
defer C.libraw_dcraw_clear_mem(img)
if goResult(result) != nil {
return nil, fmt.Errorf("failed to import file [%v]", path)
}
dataBytes := make([]uint8, int(img.data_size))
start := unsafe.Pointer(&img.data)
size := unsafe.Sizeof(uint8(0))
for i := 0; i < int(img.data_size); i++ {
item := *(*uint8)(unsafe.Pointer(uintptr(start) + size*uintptr(i)))
dataBytes[i] = item
}
rawImage := rawImg{
Height: int(img.height),
Width: int(img.width),
DataSize: int(img.data_size),
Bits: uint(img.bits),
Data: dataBytes,
}
fullbytes := rawImage.fullBytes()
return ppm.Decode(bytes.NewReader(fullbytes))
}
// Reads a RAW image file from file system and exports it to PPM format
func ExportPPM(inputPath string, exportPath string) error {
if _, err := os.Stat(exportPath); err == nil {
return fmt.Errorf("output file [%v] already exists", exportPath)
}
if _, err := os.Stat(inputPath); err != nil {
return fmt.Errorf("input file [%v] does not exist", exportPath)
}
librawProcessor := lrInit()
defer C.libraw_recycle(librawProcessor)
err := goResult(C.libraw_open_file(librawProcessor, C.CString(inputPath)))
if err != nil {
return fmt.Errorf("failed to open file [%v]", inputPath)
}
err = goResult(C.libraw_unpack(librawProcessor))
if err != nil {
return fmt.Errorf("failed to unpack file [%v]", inputPath)
}
err = goResult(C.libraw_dcraw_process(librawProcessor))
if err != nil {
return fmt.Errorf("failed to import file [%v]", inputPath)
}
if err = goResult(C.libraw_dcraw_ppm_tiff_writer(librawProcessor, C.CString(exportPath))); err != nil {
return fmt.Errorf("failed to export file to [%v]", exportPath)
}
return nil
}
func lrClose(iprc *C.libraw_data_t) {
C.libraw_close(iprc)
}