-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathRPiCamera.java
More file actions
1185 lines (1090 loc) · 41.5 KB
/
RPiCamera.java
File metadata and controls
1185 lines (1090 loc) · 41.5 KB
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
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
import java.awt.image.BufferedImage;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.util.*;
import javax.imageio.ImageIO;
import com.hopding.jrpicam.enums.AWB;
import com.hopding.jrpicam.enums.DRC;
import com.hopding.jrpicam.enums.Encoding;
import com.hopding.jrpicam.enums.Exposure;
import com.hopding.jrpicam.enums.ImageEffect;
import com.hopding.jrpicam.enums.MeteringMode;
import com.hopding.jrpicam.exceptions.FailedToRunRaspistillException;
/**
* RPiCamera is used to access the Raspberry Pi Camera and take still photos.
* There are a variety of different settings that may be changed to modify images
* taken by an RPiCamera instance, such as width, height, AWB, DRC, and the resulting
* image's encoding type (jpg, bmp, png, gif).
* <p>
* Although it provides additional features and support for Java specific operations,
* RPiCamera is essentially a wrapper class for executing the raspistill software from
* within a Java application. Thus it is essential that all Raspberry Pis using the JRPiCam
* library have properly configured and working raspistill software.
* <p>
* Usage Example:
* <pre>
*{@code
* // Directory to save pictures to
* String saveDir = "/home/pi/Pictures";
*
* // Create RPiCamera instance
* RPiCamera camera = new RPiCamera(saveDir)
* .setWidth(150) // Set width property of RPiCamera.
* .setHeight(150); // Set height property of RPiCamera.
*
* // Take a picture and save it as "/home/pi/Pictures/APicture.jpg"
* camera.takeStill("APicture.jpg");
*}
* </pre>
*
* @author Andrew Dillon
*/
public class RPiCamera {
private String prevCommand;
private String saveDir;
private HashMap<String, String[]> options = new HashMap<>();
private ProcessBuilder pb;
private Process p;
private static final int DEFAULT_WIDTH = 500;
private static final int DEFAULT_HEIGHT = 500;
/**
* Creates new RPiCamera. The resulting RPiCamera's save directory will be set to
* "/home/pi/Pictures" and will have a default image width and height of 500.
* @throws FailedToRunRaspistillException
*/
public RPiCamera() throws FailedToRunRaspistillException {
this("/home/pi/Pictures");
}
/**
* Creates new RPiCamera and sets its save directory. The resulting RPiCamera will have a
* default image width and height of 500.
*
* @param saveDir A String specifying the directory for RPiCamera to save images.
* @throws FailedToRunRaspistillException
*/
public RPiCamera(String saveDir) throws FailedToRunRaspistillException {
this.saveDir = saveDir;
try {
pb = new ProcessBuilder("raspistill");
pb.start();
} catch (IOException e) {
// The IOException was most likely thrown because raspistill isn't installed
// and/or configured properly, so throw FailedToRunRaspistillException to
// indicate that.
throw new FailedToRunRaspistillException(
"RPiCamera failed to run raspistill. The JRPiCam library relies on"
+ "raspistill to function. Please ensure it is installed and configured"
+ "on your system.");
}
// Set default width and height of images
this.setWidth(DEFAULT_WIDTH);
this.setHeight(DEFAULT_HEIGHT);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////// Image Taking Methods //////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Takes an image of the specified width and height and saves it under the
* specified name to the RPiCamera's save directory ("/home/pi/Pictures" by
* default). The image's encoding will be the same as the RPiCamera's encoding
* setting (JPEG by default).
* <p>
* Usage Example:
* <pre>
*{@code
* // Create an RPiCamera instance using default constructor, which sets the
* // save directory to "/home/pi/Pictures".
* RPiCamera piCamera = new RPiCamera()
* .setEncoding(Encoding.PNG); // Change encoding from JPEG to PNG
*
* // Take a 500x500 PNG image and save it as "/home/pi/Pictures/AStillImage.png"
* piCamera.takeStill("AStillImage.png", 500, 500);
*}
* </pre>
*
* @param pictureName A String containing the name to save picture under.
* @param width An int specifying the width of the image to take.
* @param height An int specifying the height of the image to take.
* @return A File object representing the full path the picture was saved to.
* @throws IOException
* @throws InterruptedException
*/
public File takeStill(String pictureName, int width, int height) throws IOException, InterruptedException {
List<String> command = new ArrayList<>();
command.add("raspistill");
command.add("-o");
command.add(saveDir + File.separator + pictureName);
command.add("-w");
command.add("" + width);
command.add("-h");
command.add("" + height);
for (Map.Entry<String, String[]> entry : options.entrySet()) {
if (entry.getValue() != null &&
!"width".equals(entry.getKey()) &&
!"height".equals(entry.getKey())) {
command.addAll(Arrays.asList(entry.getValue()));
}
}
prevCommand = command.toString();
pb = new ProcessBuilder(command);
// System.out.println("Executed this command:\n\t" + command.toString());
// pb.redirectErrorStream(true);
// pb.redirectOutput(
// new File(System.getProperty("user.home") + File.separator +
// "Desktop" + File.separator + "RPiCamera.out"));
p = pb.start();
p.waitFor();
return new File(saveDir + File.separator + pictureName);
}
/**
* Takes an image and saves it under the specified name to the RPiCamera's save
* directory ("/home/pi/Pictures" by default). The image's encoding will be the
* same as the RPiCamera's encoding setting (JPEG by default).
* <p>
* Usage Example:
* <pre>
*{@code
* // Create an RPiCamera instance using default constructor, which sets the
* // save directory to "/home/pi/Pictures".
* RPiCamera piCamera = new RPiCamera()
* .setEncoding(Encoding.PNG); // Change encoding from JPEG to PNG
*
* // Take a PNG image and save it as "/home/pi/Pictures/AStillImage.png"
* piCamera.takeStill("AStillImage.png");
*}
* </pre>
*
* @param pictureName A String containing the name to save picture under.
* @return A File object representing the full path the picture was saved to.
* @throws IOException
* @throws InterruptedException
*/
public File takeStill(String pictureName) throws IOException, InterruptedException {
return takeStill(pictureName,
Integer.parseInt(options.get("width")[1]),
Integer.parseInt(options.get("height")[1]));
}
/**
* Takes an image of the specified width and height and stores it in a BufferedImage
* object. The resulting image is NOT saved anywhere in the Pi's memory. The image's
* encoding will be the same as the RPiCamera's encoding setting (JPEG by default).
* <p>
* Usage Example:
* <pre>
*{@code
* // Create an RPiCamera instance using default constructor, which sets the
* // save directory to "/home/pi/Pictures".
* RPiCamera piCamera = new RPiCamera()
* .setEncoding(Encoding.PNG); // Change encoding from JPEG to PNG
*
* // Take a 500x500 PNG image and store it in a BufferedImage
* BufferedImage buffImg = piCamera.takeBufferedStill(500, 500);
*}
* </pre>
*
* @param width An int specifying width of image to take.
* @param height An int specifying height of image to take.
* @return A BufferedImage containing the image.
* @throws IOException
* @throws InterruptedException
*/
public BufferedImage takeBufferedStill(int width, int height) throws IOException, InterruptedException {
List<String> command = new ArrayList<>();
command.add("raspistill");
command.add("-o");
command.add("-v");
command.add("-w");
command.add("" + width);
command.add("-h");
command.add("" + height);
for (Map.Entry<String, String[]> entry : options.entrySet()) {
if (entry.getValue() != null &&
!entry.getKey().equals("width") &&
!entry.getKey().equals("height")) {
Collections.addAll(command, entry.getValue());
}
}
prevCommand = command.toString();
pb = new ProcessBuilder(command);
// System.out.println("Executed this command:\n\t" + command.toString());
// pb.redirectErrorStream(true);
// pb.redirectOutput(
// new File(System.getProperty("user.home") + File.separator +
// "Desktop" + File.separator + "RPiCamera.out"));
p = pb.start();
BufferedImage bi = ImageIO.read(p.getInputStream());
// --------------------------------------------------------------------------
// This code can be used to specify an ImageReader - perhaps for a specific
// type of image - in place of the previous line:
//
// ImageInputStream iis = ImageIO.createImageInputStream(p.getInputStream());
// Iterator<?> imgReaders = ImageIO.getImageReadersByFormatName("png");
// ImageReader reader = (ImageReader) imgReaders.next();
// reader.setInput(iis, true); // May need to set this to false...
// ImageReadParam param = reader.getDefaultReadParam();
// BufferedImage bi = reader.read(0, param);
// --------------------------------------------------------------------------
p.getInputStream().close();
return bi;
}
/**
* Takes an image and stores it in a BufferedImage object. The resulting image is
* NOT saved anywhere in the Pi's memory. The image's encoding will be the same as
* the RPiCamera's encoding setting (JPEG by default).
* <p>
* Usage Example:
* <pre>
*{@code
* // Create an RPiCamera instance using default constructor, which sets the
* // save directory to "/home/pi/Pictures".
* RPiCamera piCamera = new RPiCamera()
* .setEncoding(Encoding.PNG); // Change encoding from JPEG to PNG
*
* // Take a PNG image and store it in a BufferedImage
* BufferedImage buffImg = piCamera.takeBufferedStill();
*}
* </pre>
* @return A BufferedImage containing the image.
* @throws IOException
* @throws InterruptedException
*/
public BufferedImage takeBufferedStill() throws IOException, InterruptedException {
return takeBufferedStill(
Integer.parseInt(options.get("width")[1]),
Integer.parseInt(options.get("height")[1]));
}
/**
* Captures an image and returns the RGB values of that image. Images taken in this manner
* are not encoded, rather rather their RGB values are stored in an int array. Indexes 0 - 2
* of the array make up the red, green, and blue values of the upper left pixel in the image.
* The pixels are added to the array from left to right, top to bottom.
* <p>
* If an image's dimensions are not multiples of 16, then black padding will be added onto
* the right and bottom sides of an image until both the width and height are multiples of 16
* (e.g. a 500x500 image would have a black band 12 pixels thick added to the right and bottom
* sides to achieve dimensions of 512x512, the closest multiple of 16 to 500).
* <p>
* The boolean keepPadding parameter may be set to false to remove this padding. Note that this does
* require extra computations to be performed, so it has the potential to slightly reduce the fps
* a Pi is capable of. However, this has not yet been officially tested for. If the padding is kept,
* then it is the responsibility of the caller to determine what the dimensions of the resulting image
* will be, and adjust their processing as such (e.g. If the padding is kept on a 500x500 image, the
* array that is returned will contain 786,432 elements, as opposed to the 750,000 one would expect from
* a 500x500 image. This is because the image stored in the returned array is not 500x500, but 512x512 and
* the caller of this method must take that into account when processing the image).
* <p>
* Usage Example:
* <pre>
*{@code
* // Creating and setting up an RPiCamera
* RPiCamera piCamera = new RPiCamera()
* .turnOffPreview()
* .setTimeout(1);
*
* // Capture an image and store its RGB values, not preserving the border
* int[] rgbVals = piCamera.takeStillAsRGB(500, 500, false);
*
* // Create a BufferedImage from the stored RGB values
* BufferedImage image = new BufferedImage(500, 500, BufferedImage.TYPE_INT_RGB);
* WritableRaster raster = (WritableRaster) image.getData();
* raster.setPixels(0, 0, 500, 500, rgbVals);
* image.setData(raster);
*
* // Write the BufferedImage to a file as a JPEG
* File file = new File("/home/pi/Desktop/A Cool Image.jpg");
* ImageIO.write(image, "jpg", file);
* }
* </pre>
*
* @param width An int specifying the width of the image to take (ideally a multiple of 16).
* @param height An int specifying the height of the image to take (ideally a multiple of 16).
* @param keepPadding A boolean indicating whether or not to preserve the padding on an image.
* @return An int array containing the image's RGB values.
* @throws IOException
*/
public int[] takeStillAsRGB(int width, int height, boolean keepPadding) throws IOException {
List<String> command = new ArrayList<>();
command.add("raspiyuv");
command.add("-rgb");
command.add("-o");
command.add("-v");
command.add("-w");
command.add("" + width);
command.add("-h");
command.add("" + height);
for (Map.Entry<String, String[]> entry : options.entrySet()) {
if (entry.getValue() != null &&
!entry.getKey().equals("width") &&
!entry.getKey().equals("height")) {
Collections.addAll(command, entry.getValue());
}
}
prevCommand = command.toString();
pb = new ProcessBuilder(command);
// System.out.println("Executed this command:\n\t" + command.toString());
// pb.redirectErrorStream(true);
// pb.redirectOutput(
// new File(System.getProperty("user.home") + File.separator +
// "Desktop" + File.separator + "RPiCamera.out"));
p = pb.start();
BufferedInputStream inputStream = new BufferedInputStream(p.getInputStream());
// Calculate the width and height of the image with padding, if dimensions
// aren't multiples of 16
int paddedWidth = width;
int paddedHeight = height;
int widthRemainder = width % 16;
if (widthRemainder != 0)
paddedWidth = width + 16 - widthRemainder;
int heightRemainder = height % 16;
if (heightRemainder != 0)
paddedHeight = height + 16 - heightRemainder;
// Create an int array to hold RGB values of the image, size will differ
// depending upon whether or not the image will have padding
int[] rgbVals;
if (!keepPadding)
rgbVals = new int[width * height * 3]; // times three because each pixel is made up of three bytes,
// and each element holds one byte (an R, G, or B value)
else
rgbVals = new int[paddedWidth * paddedHeight * 3];
int rgbData;
int pos = 0;
// Read the image into the array, but avoid storing pixels that make up padding
if (!keepPadding) {
int storedBytes = 0;
int columnPos = 1;
int areaWithoutPadding = width * height;
while ((rgbData = inputStream.read()) != -1) {
if ((columnPos / 3d) <= width) {
rgbVals[storedBytes] = rgbData;
storedBytes++;
}
columnPos++;
if (columnPos == (paddedWidth * 3) + 1)
columnPos = 1;
if ((storedBytes / 3d) == areaWithoutPadding)
break;
}
}
// Just read the image into the array, and don't worry about the padding
else {
while ((rgbData = inputStream.read()) != -1) {
rgbVals[pos] = rgbData;
pos++;
}
}
inputStream.close();
return rgbVals;
}
/**
* Captures an image and returns the RGB values of that image. Images taken in this manner
* are not encoded, rather rather their RGB values are stored in an int array. Indexes 0 - 2
* of the array make up the red, green, and blue values of the upper left pixel in the image.
* The pixels are added to the array from left to right, top to bottom.
* <p>
* If an image's dimensions are not multiples of 16, then black padding will be added onto
* the right and bottom sides of an image until both the width and height are multiples of 16
* (e.g. a 500x500 image would have a black band 12 pixels thick added to the right and bottom
* sides to achieve dimensions of 512x512, the closest multiple of 16 to 500).
* <p>
* The boolean keepPadding parameter may be set to false to remove this padding. Note that this does
* require extra computations to be performed, so it has the potential to slightly reduce the fps
* a Pi is capable of. However, this has not yet been officially tested for. If the padding is kept,
* then it is the responsibility of the caller to determine what the dimensions of the resulting image
* will be, and adjust their processing as such (e.g. If the padding is kept on a 500x500 image, the
* array that is returned will contain 786,432 elements, as opposed to the 750,000 one would expect from
* a 500x500 image. This is because the image stored in the returned array is not 500x500, but 512x512 and
* the caller of this method must take that into account when processing the image).
* <p>
* Usage Example:
* <pre>
*{@code
* // Creating and setting up an RPiCamera
* RPiCamera piCamera = new RPiCamera()
* .turnOffPreview()
* .setTimeout(1);
*
* // Capture an image and store its RGB values, not preserving the border
* int[] rgbVals = piCamera.takeStillAsRGB(500, 500, false);
*
* // Create a BufferedImage from the stored RGB values
* BufferedImage image = new BufferedImage(500, 500, BufferedImage.TYPE_INT_RGB);
* WritableRaster raster = (WritableRaster) image.getData();
* raster.setPixels(0, 0, 500, 500, rgbVals);
* image.setData(raster);
*
* // Write the BufferedImage to a file as a JPEG
* File file = new File("/home/pi/Desktop/A Cool Image.jpg");
* ImageIO.write(image, "jpg", file);
*}
* </pre>
*
* @param keepPadding A boolean indicating whether or not to preserve the padding on an image.
* @return An int array containing the image's RGB values.
* @throws IOException
*/
public int[] takeStillAsRGB(boolean keepPadding) throws IOException {
return takeStillAsRGB(
Integer.parseInt(options.get("width")[1]),
Integer.parseInt(options.get("height")[1]),
keepPadding);
}
/**
* Take a series of timelapsed photos for the specified time frame and save them under the
* specified filename to the RPiCamera's save directory. Length of time to timelapse for may
* be specified with the {@link #setTimeout(int)} method.
* <p>
* THIS METHOD WILL BLOCK THE THREAD UNTIL THE TIMELAPSE IS COMPLETE IF boolean wait ARGUMENT
* IS SET TO true.
* <p>
* Most recent image in the series may be appended to the file specified in the
* {@link #setLinkLatestImage(boolean, String)} method. If this property is set, timelapse() will return a File
* object representing the full path to the file the timelapsed photos are being linked to.
* Otherwise, will return null.
* <p>
* When passing in a String for the photo's name, it is important to include "%04d" in the name.
* This is where the frame count number will be included in the file name. If the name does not
* contain these characters, the frame count will be appended to the beginning of each file name.
* <p>
* Usage Example:
* <pre>
* {@code
* // Create an RPiCamera instance using default constructor, which sets the
* // save directory to "/home/pi/Pictures".
* RPiCamera piCamera = new RPiCamera()
* .setEncoding(Encoding.PNG) // Change encoding from JPEG to PNG
* .setTimeout(10000) // Set period for timelapse to run at 10 seconds
* .setLinkLatestImage(true, "/home/pi/Pictures/linkfile.png"); // Set RPiCamera to link images to "linkFile.png"
* // Begin timelapse
* piCamera.timelapse(
* true, // Block thread until timelapse is completed
* "%04dTimelapsePics.png", // Save images under this name, with frame count added to beginning
* 1000 // Wait 1 second between capturing each image
* );
* }
* </pre>
*
* @param wait A boolean indicating whether to block the thread until timelapse is complete or not.
* @param pictureName A String containing name for each captured image.
* @param time Period of time in milliseconds to wait between taking each image.
* @return If the link latest image property is set, the File images are being linked to,
* otherwise returns null.
* @throws IOException
* @throws InterruptedException
*/
public File timelapse(boolean wait, String pictureName, int time) throws IOException, InterruptedException {
if (!pictureName.contains("%04d"))
pictureName = "%04d" + pictureName;
List<String> command = new ArrayList<>();
command.add("raspistill");
command.add("-tl");
command.add("" + time);
command.add("-o");
command.add(saveDir + File.separator + pictureName);
for (Map.Entry<String, String[]> entry : options.entrySet()) {
if (entry.getValue() != null)
Collections.addAll(command, entry.getValue());
}
prevCommand = command.toString();
pb = new ProcessBuilder(command);
// System.out.println("Executed this command:\n\t" + command.toString());
// pb.redirectErrorStream(true);
// pb.redirectOutput(
// new File(System.getProperty("user.home") + File.separator +
// "Desktop" + File.separator + "RPiCamera.out"));
p = pb.start();
if (wait)
p.waitFor();
// if (!options.get("latest").equals(null)) {
try {
return new File(options.get("latest")[1]);
} catch (NullPointerException e) {
return null;
}
// } else
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////// End of Image Taking Methods /////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Stops any raspistill processes being run by RPiCamera.
*/
public void stop() {
if (p != null)
p.destroy();
}
/**
* Gets the raspistill command previously executed by the RPiCamera. If no commands have
* been executed, null will be returned.
*
* @return String containing command previously executed.
*/
public String getPrevCommand() {
return prevCommand.substring(1, prevCommand.lastIndexOf("]")).replaceAll(",", "");
}
/**
* Sets the RPiCamera's save directory.
*
* @param saveDir String containing directory for RPiCamera to save images to.
*/
public RPiCamera setSaveDir(String saveDir) {
this.saveDir = saveDir;
return this;
}
// public String getCameraSettings() {
// List<String> command = new ArrayList<String>();
// command.add("raspistill");
// command.add("-set");
// pb = new ProcessBuilder(command);
// prevCommand = command.toString();
// try {
// p = pb.start();
// BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
// String settings;
// while ((settings = br.readLine()) != null) {
// }
// br.close();
// return settings;
// } catch (IOException e) {
// e.printStackTrace();
// }
// // If this point is reached, IOException was thrown and failed to retrieve settings,
// // so return null.
// return null;
// }
/**
* Sets all RPiCamera options to their default settings, overriding any previously
* set options.
*/
public RPiCamera setToDefaults() {
saveDir = "/home/pi/Pictures";
for (Map.Entry<String, String[]> entry : options.entrySet()) {
entry.setValue(null);
}
return this;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////// PREVIEW PARAMETER COMMANDS ///////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Turns off image preview.
*/
public RPiCamera turnOffPreview() {
options.put("preview", new String[] { "-n" });
return this;
}
/**
* Turns on image preview. Note the preview will be superimposed over the top of
* any other windows/graphics.
*/
public RPiCamera turnOnPreview() {
options.put("preview", null);
return this;
}
/**
* Runs the preview windows using the full resolution capture mode. Maximum frames
* per second in this mode is 15fps and the preview will have the same field of view as
* the capture. Captures should happen more quickly as no mode change should be required.
* This feature is currently under development.
*/
public RPiCamera setFullPreviewOn() {
options.put("fullpreview", new String[] { "-fp" });
return this;
}
/**
* Turns off fullpreview mode.
*/
public RPiCamera setFullPreviewOff() {
options.put("fullpreview", null);
return this;
}
/**
* Defines the size and location on the screen that the preview window will be placed.
* Note the preview will be superimposed over the top of any other windows/graphics.
*
* @param x int coordinate for upper right corner of preview window.
* @param y int coordinate for upper right corner of preview window.
* @param w An int specifying width of preview window.
* @param h An int specifying height of preview window.
*/
public RPiCamera turnOnPreview(int x, int y, int w, int h) {
options.put("preview", new String[] { "-p", "" + x + "," + y + "," + w + "," + h });
return this;
}
/**
* Turn fullscreen preview on or off.
*
* @param fullscreen turn on/off fullscreen.
*/
public RPiCamera setPreviewFullscreen(boolean fullscreen) {
if (fullscreen)
options.put("fullscreen", new String[] { "-f" });
else
options.put("fullscreen", null);
return this;
}
/**
* Sets the preview window's opacity (0 to 255). Opacity values lower than 0
* will set opacity to 0, values greater than 255 will set opacity to 255.
*
* @param opacity An integer specifying the opacity.
*/
public RPiCamera setPreviewOpacity(int opacity) {
if (opacity > 255)
opacity = 255;
else if (opacity < 0)
opacity = 0;
options.put("opacity", new String[] { "-op", "" + opacity });
return this;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////// END OF PREVIEW PARAMETER COMMANDS ////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////// IMAGE PARAMETER COMMANDS /////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Sets sharpness of image (-100 to 100), default value is 0. Sharpness values
* lower than -100 will set sharpness to -100, values greater than 100 will set
* sharpness to 100.
*
* @param sharpness An int specifying the sharpness.
*/
public RPiCamera setSharpness(int sharpness) {
if (sharpness > 100)
sharpness = 100;
else if (sharpness < -100)
sharpness = -100;
options.put("sharpness", new String[] { "-sh", "" + sharpness });
return this;
}
/**
* Sets contrast of image (-100 to 100), default value is 0. Contrast values lower
* than -100 will set contrast to -100, values greater than 100 will set contrast
* to 100.
*
* @param contrast An int specifying the contrast.
*/
public RPiCamera setContrast(int contrast) {
if (contrast > 100)
contrast = 100;
else if (contrast < -100)
contrast = -100;
options.put("constast", new String[] { "-co", "" + contrast });
return this;
}
/**
* Sets brightness of image (0 to 100), default value is 50. 0 is black, 100 is white.
* Brightness values lower than 0 will set brightness to 0, values greater than 100 will
* set brightness to 100.
*
* @param brightness An int specifying the brightness.
*/
public RPiCamera setBrightness(int brightness) {
if (brightness > 100)
brightness = 100;
else if (brightness < 0)
brightness = 0;
options.put("brightness", new String[] { "-br", "" + brightness });
return this;
}
/**
* Sets colour saturation of image (-100 to 100), default is 0. Saturation values lower
* than -100 will set saturation to -100, values greater than 100 will set saturation to
* 100.
*
* @param saturation An int specifying the saturation.
*/
public RPiCamera setSaturation(int saturation) {
if (saturation > 100)
saturation = 100;
else if (saturation < -100)
saturation = -100;
options.put("saturation", new String[] { "-sa", "" + saturation });
return this;
}
/**
* WARNING: OPERATION NOT YET SUPPORTED BY raspistill SOFTWARE. OPTION MAY STILL BE SET,
* BUT WILL HAVE NO EFFECT ON THE IMAGE UNTIL SUPPORT IS ADDED TO raspistill.<br>
* <br>
* Sets ISO of RPiCamera (100 to 800).
*
* @param iso An int specifying the ISO.
*/
public RPiCamera setISO(int iso) {
options.put("ISO", new String[] { "-ISO", "" + iso });
return this;
}
// // THIS IS ONLY USED FOR VIDEOS, NOT PICTURES
// private void turnOnVStab() {
// options.put("vstab", new String[] { "-vs" });
// }
// // THIS IS ONLY USED FOR VIDEOS, NOT PICTURES
// public void turnOffVStab() {
// options.put("vstab", null);
// }
// TODO: MAKE METHOD FOR EVCOMPENSATION (-25 - 25?), ONLY FOR VIDEO
/**
* Sets exposure mode of RPiCamera. Certain modes may not be supported
* by raspistill software, depending on camera tuning.
*
* @param exposure An Exposure enum specifying the desired mode.
*/
public RPiCamera setExposure(Exposure exposure) {
options.put("exposure", new String[] { "-ex", exposure.toString() });
return this;
}
/**
* Sets Automatic White Balance (AWB) mode. Certain modes may not
* be supported by raspistill software, depending on camera type.
*
* @param awb An AWB enum specifying the desired AWB setting.
*/
public RPiCamera setAWB(AWB awb) {
options.put("awb", new String[] { "-awb", awb.toString() });
return this;
}
/**
* Sets an effect to be applied to image. Certain settings may not be
* supported by raspistill software, depending on circumstances.
*
* @param imageEffect An ImageEffect enum specifying the desired effect.
*/
public RPiCamera setImageEffect(ImageEffect imageEffect) {
options.put("imxfx", new String[] { "-ifx", imageEffect.toString() });
return this;
}
/**
* Sets colour effect of RPiCamera. The specified U and V parameters (0 to 255)
* are applied to the U and Y channels of the image.
* For example, setColourEffect(128, 128) should result in a monochrome image.
*
* @param U
* @param V
*/
public RPiCamera setColourEffect(int U, int V) {
if (U > 255)
U = 255;
else if (U < 0)
U = 0;
if (V > 255)
V = 255;
else if (V < 0)
V = 0;
options.put("colfx", new String[] { "-cfx", "" + U, ":", "" + V });
return this;
}
/**
* Sets metering mode used for preview and capture.
*
* @param meteringMode a MeteringMode enum specifying the desired mode.
*/
public RPiCamera setMeteringMode(MeteringMode meteringMode) {
options.put("metering", new String[] { "-mm", meteringMode.toString() });
return this;
}
/**
* Sets the rotation of the image in viewfinder and resulting image. This
* can take any value from 0 upwards, but due to hardware constraints only
* 0, 90, 180 and 270 degree rotations are supported.
*
* @param rotation
*/
public RPiCamera setRotation(int rotation) {
if (rotation > 359)
while (rotation > 359)
rotation = rotation - 360;
else if (rotation < 0)
while (rotation < 0)
rotation = rotation + 360;
options.put("rotation", new String[] { "-rot", "" + rotation });
return this;
}
/**
* Flips the preview and saved image horizontally.
*/
public RPiCamera setHorizontalFlipOn() {
options.put("hflip", new String[] { "-hf" });
return this;
}
/**
* Turns off horizontal flip.
*/
public RPiCamera setHorizontalFlipOff() {
options.put("hflip", null);
return this;
}
/**
* Flips the preview and saved image vertically.
*/
public RPiCamera setVerticalFlipOn() {
options.put("vflip", new String[] { "-vf" });
return this;
}
/**
* Turns off vertical flip.
*/
public RPiCamera setVerticalFlipOff() {
options.put("vflip", null);
return this;
}
/**
* Allows the specification of the area of the sensor to be used as the source
* for the preview and capture. This is defined as x,y for the top left corner,
* and a width and height, all values in normalised coordinates (0.0-1.0). \
* <p>
* So to set a ROI at half way across and down the sensor, and a width and height of a
* quarter of the sensor use: {@code setRegionOfInterest(0.5, 0.5, 0.25, 0.25)}
*
* @param x
* @param y
* @param w
* @param d
*/
public RPiCamera setRegionOfInterest(double x, double y, double w, double d) {
if (x > 1.0)
x = 1.0;
else if (x < 0.0)
x = 0.0;
if (y > 1.0)
y = 1.0;
else if (y < 0.0)
y = 0.0;
if (w > 1.0)
w = 1.0;
else if (w < 0.0)
w = 0.0;
if (d > 1.0)
d = 1.0;
else if (d < 0.0)
d = 0.0;
options.put("roi", new String[] { "-roi", "" + x, ",", "" + y, ",", "" + w, ",", "" + d });
return this;
}
/**
* Set the shutter speed to the specified value (in microseconds). There
* is currently an upper limit of approximately 6000000us (6000ms, 6s).
* Shutter speed values passed in that are greater than 6000000 will be
* regarded as 6000000.
*
* @param speed
*/
public RPiCamera setShutter(int speed) {
if (speed > 6000000)
speed = 6000000;
if (speed < 0)
speed = 0;
options.put("shutter", new String[] { "-ss", "" + speed });
return this;
}
// TODO: MAKE METHOD FOR AWBGAINS DISPLAY?
/**
* Sets the Dynamic Range Compression of image. DRC changes the images by
* increasing the range of dark areas of the image, and decreasing the
* brighter areas. This can improve the image in low light areas.
*
* @param drc A DRC enum specifying the desired DRC level.
*/
public RPiCamera setDRC(DRC drc) {
options.put("drc", new String[] { "-drc", drc.toString() });
return this;
}
// TODO: MAKE METHOD FOR DISPLAYING STATS
// TODO: MAKE METHOD FOR ANNOTATING PICTURE WITH FLAGS AND TEXT
// TODO: MAKE METHOD FOR STEREOSCOPIC IMAGES
// TODO: MAKE METHOD FOR PREVIEW PARAMETER COMMANDS (Bottom of raspistill -? output)
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////// ENG OF IMAGE PARAMETER COMMANDS ////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////// APPLICATION SPECIFIC SETTINGS //////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Sets width of images taken by RPiCamera. Note that this setting
* can be overriden by the {@link #takeStill(String, int, int)} and {@link #takeBufferedStill(int, int)} methods.
*
* @param width An int specifying the width.
*/
public RPiCamera setWidth(int width) {
options.put("width", new String[] { "-w", "" + width });
return this;
}
/**
* Sets height of images taken by the RPiCamera. Note that this settings
* can be overriden by the {@link #takeStill(String, int, int)} and {@link #takeBufferedStill(int, int)} methods.
*
* @param height An int specifying the height.
*/
public RPiCamera setHeight(int height) {
options.put("height", new String[] { "-h", "" + height });
return this;
}
/**
* Sets quality of image (0 to 100), 75 is recommended for usual
* purposes. Quality values lower than 0 will set quality to 0, values