-
Notifications
You must be signed in to change notification settings - Fork 11
/
raspicam.go
419 lines (371 loc) · 9.35 KB
/
raspicam.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
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
// Copyright 2013, David Howden
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package raspicam provides basic Go APIs for interacting with the Raspberry Pi
// camera.
//
// All captures are prepared by first creating a CaptureCommand (Still, StillYUV or
// Vid structs via calls to the NewStill, NewStillYUV or NewVid functions respectively).
// The Capture function can then be used to perform the capture.
package raspicam
import (
"bufio"
"fmt"
"io"
"os/exec"
"strconv"
"strings"
"time"
)
// ExposureMode is an enumeration of supported exposure modes.
type ExposureMode uint
const (
ExposureOff ExposureMode = iota
ExposureAuto
ExposureNight
ExposureNightPreview
ExposureBacklight
ExposureSpotlight
ExposureSports
ExposureSnow
ExposureBeach
ExposureVerylong
ExposureFixedFPS
ExposureAntishake
ExposureFireworks
)
var exposureModes = [...]string{
"off",
"auto",
"night",
"nightpreview",
"backlight",
"spotlight",
"sports",
"snow",
"beach",
"verylong",
"fixedfps",
"antishake",
"fireworks",
}
// String returns the command line parameter for the given ExposureMode.
func (e ExposureMode) String() string { return exposureModes[e] }
// A MeteringMode specificies an exposure metering mode.
type MeteringMode uint
const (
MeteringAverage MeteringMode = iota
MeteringSpot
MeteringBacklit
MeteringMatrix
)
var exposureMeteringMode = [...]string{
"average",
"spot",
"backlit",
"matrix",
}
// String returns the command line parameter for the given MeteringMode.
func (m MeteringMode) String() string { return exposureMeteringMode[m] }
// An AWBMode is an enumeration of the auto white balance modes.
type AWBMode uint
const (
AWBOff AWBMode = iota
AWBAuto
AWBSunlight
AWBCloudy
AWBShade
AWBTungsten
AWBFluorescent
AWBIncandescent
AWBFlash
AWBHorizon
)
var awbModes = [...]string{
"off",
"auto",
"sun",
"cloud",
"shade",
"tungsten",
"fluorescent",
"incandescent",
"flash",
"horizon",
}
// String returns the command line parameter for the given AWBMode.
func (a AWBMode) String() string { return awbModes[a] }
// An ImageFX specifies an image effect for the camera.
type ImageFX uint
const (
FXNone ImageFX = iota
FXNegative
FXSolarize
FXPosterize
FXWhiteboard
FXBlackboard
FXSketch
FXDenoise
FXEmboss
FXOilpaint
FXHatch
FXGpen
FXPastel
FXWatercolour
FXFilm
FXBlur
FXSaturation
FXColourSwap
FXWashedOut
FXPosterise
FXColourPoint
FXColourBalance
FXCartoon
)
var imageFXModes = [...]string{
"none",
"negative",
"solarise",
"sketch",
"denoise",
"emboss",
"oilpaint",
"hatch",
"gpen",
"pastel",
"watercolour",
"film",
"blur",
"saturation",
"colourswap",
"washedout",
"posterise",
"colourpoint",
"colourbalance",
"cartoon",
}
// String returns the command-line parameter for the given imageFX.
func (i ImageFX) String() string { return imageFXModes[i] }
// ColourFX represents colour effects parameters.
type ColourFX struct {
Enabled bool
U, V int
}
// String returns the command parameter for the given ColourFX.
func (c ColourFX) String() string {
return fmt.Sprintf("%v:%v", c.U, c.V)
}
// FloatRect contains the information necessary to construct a rectangle
// with dimensions in floating point.
type FloatRect struct {
X, Y, W, H float64
}
// String returns the command parameter for the given FloatRect.
func (r *FloatRect) String() string {
return fmt.Sprintf("%v, %v, %v, %v", r.X, r.Y, r.W, r.H)
}
// The default RegionOfInterest setup.
var defaultRegionOfInterest = FloatRect{W: 1.0, H: 1.0}
// Camera represents a camera configuration.
type Camera struct {
Sharpness int // -100 to 100
Contrast int // -100 to 100
Brightness int // 0 to 100
Saturation int // -100 to 100
ISO int // TODO: what range? (see RaspiCamControl.h)
VideoStabilisation bool
ExposureCompensation int // -10 to 10? (see RaspiCamControl.h)
ExposureMode ExposureMode
MeteringMode MeteringMode
AWBMode AWBMode
ImageEffect ImageFX
ColourEffects ColourFX
Rotation int // 0 to 359
HFlip, VFlip bool
RegionOfInterest FloatRect // Assumes Normalised to [0.0,1.0]
ShutterSpeed time.Duration
}
// The default Camera setup.
var defaultCamera = Camera{
Brightness: 50,
ISO: 400,
ExposureMode: ExposureAuto,
MeteringMode: MeteringAverage,
AWBMode: AWBAuto,
ImageEffect: FXNone,
ColourEffects: ColourFX{U: 128, V: 128},
RegionOfInterest: defaultRegionOfInterest,
}
// String returns the parameters necessary to construct the
// equivalent command line arguments for the raspicam tools.
func (c *Camera) String() string {
return paramString(c)
}
// params is a wrapper around a string slice which adds convenience
// methods for adding different types of parameters
type params []string
func (ps *params) add(xs ...string) { *ps = append(*ps, xs...) }
func (ps *params) addInt(x string, n int) { *ps = append(*ps, x, strconv.Itoa(n)) }
func (ps *params) addInt64(x string, n int64) { *ps = append(*ps, x, strconv.FormatInt(n, 10)) }
func paramString(x interface{ params() []string }) string {
return strings.Join(x.params(), " ")
}
func (c *Camera) params() []string {
var out params
if c.Sharpness != defaultCamera.Sharpness {
out.addInt("--sharpness", c.Sharpness)
}
if c.Contrast != defaultCamera.Contrast {
out.addInt("--contrast", c.Contrast)
}
if c.Brightness != defaultCamera.Brightness {
out.addInt("--brightness", c.Brightness)
}
if c.Saturation != defaultCamera.Saturation {
out.addInt("--saturation", c.Saturation)
}
if c.ISO != defaultCamera.ISO {
out.addInt("--ISO", c.ISO)
}
if c.VideoStabilisation {
out.add("--vstab")
}
if c.ExposureCompensation != defaultCamera.ExposureCompensation {
out.addInt("--ev", c.ExposureCompensation)
}
if c.ExposureMode != defaultCamera.ExposureMode {
out.add("--exposure", c.ExposureMode.String())
}
if c.MeteringMode != defaultCamera.MeteringMode {
out.add("--metering", c.MeteringMode.String())
}
if c.AWBMode != defaultCamera.AWBMode {
out.add("--awb", c.AWBMode.String())
}
if c.ImageEffect != defaultCamera.ImageEffect {
out.add("--imxfx", c.ImageEffect.String())
}
if c.ColourEffects.Enabled {
out.add("--colfx", c.ColourEffects.String())
}
if c.MeteringMode != defaultCamera.MeteringMode {
out.add("--metering", c.MeteringMode.String())
}
if c.Rotation != defaultCamera.Rotation {
out.addInt("--rotation", c.Rotation)
}
if c.HFlip {
out.add("--hflip")
}
if c.VFlip {
out.add("--vflip")
}
if c.RegionOfInterest != defaultCamera.RegionOfInterest {
out.add("--roi", c.RegionOfInterest.String())
}
if c.ShutterSpeed != defaultCamera.ShutterSpeed {
out.addInt64("--shutter", int64(c.ShutterSpeed/time.Microsecond))
}
return out
}
// Rect represents a rectangle defined by integer parameters.
type Rect struct {
X, Y, Width, Height uint32
}
// String returns the parameter string for the given Rect.
func (r *Rect) String() string {
return fmt.Sprintf("%v, %v, %v, %v", r.X, r.Y, r.Width, r.Height)
}
// PreviewMode represents an enumeration of preview modes.
type PreviewMode uint
const (
PreviewFullscreen PreviewMode = iota // Enabled by default
PreviewWindow
PreviewDisabled
)
var previewModes = [...]string{
"fullscreen",
"preview",
"nopreview",
}
// String returns the parameter string for the given PreviewMode.
func (p PreviewMode) String() string { return previewModes[p] }
// Preview contains the settings for the camera previews.
type Preview struct {
Mode PreviewMode
Opacity int // Opacity of window (0 = transparent, 255 = opaque)
Rect Rect // Used when Mode is PreviewWindow
}
// The default Preview setup.
var defaultPreview = Preview{
Mode: PreviewFullscreen,
Opacity: 255,
Rect: Rect{X: 0, Y: 0, Width: 1024, Height: 768},
}
// String returns the parameter string for the given Preview.
func (p *Preview) String() string {
return paramString(p)
}
func (p *Preview) params() []string {
var out params
if p.Mode == PreviewWindow {
out.add("--"+p.Mode.String(), p.Rect.String())
} else {
if p.Mode != defaultPreview.Mode {
out.add("--" + p.Mode.String())
}
}
if p.Opacity != defaultPreview.Opacity {
out.addInt("--opacity", p.Opacity)
}
return out
}
// CaptureCommand represents a prepared capture command.
type CaptureCommand interface {
Cmd() string
Params() []string
}
// Capture runs the given CaptureCommand and writes the result to the given
// writer. Any errors are sent back on the given error channel, which is closed
// before the function returns.
func Capture(c CaptureCommand, w io.Writer, errCh chan<- error) {
done := make(chan struct{})
defer func() {
<-done
close(errCh)
}()
cmd := exec.Command(c.Cmd(), c.Params()...)
stdout, err := cmd.StdoutPipe()
if err != nil {
errCh <- err
return
}
stderr, err := cmd.StderrPipe()
if err != nil {
errCh <- err
return
}
go func() {
errScanner := bufio.NewScanner(stderr)
for errScanner.Scan() {
errCh <- fmt.Errorf("%v: %v", c.Cmd(), errScanner.Text())
}
if err := errScanner.Err(); err != nil {
errCh <- err
}
close(done)
}()
if err := cmd.Start(); err != nil {
errCh <- fmt.Errorf("starting: %v", err)
return
}
defer func() {
if err := cmd.Wait(); err != nil {
errCh <- fmt.Errorf("waiting: %v", err)
}
}()
_, err = io.Copy(w, stdout)
if err != nil {
errCh <- err
}
}