16 Controlling Photo Capture with Buttons
This tutorial teaches how to control the camera for taking photos and recording videos by adding buttons to the page. Similar to previous tutorials, images are by default saved in the static folder, and videos are saved in the videos folder.
Preparation
Since the product automatically runs the main program at startup, which occupies the camera resource, this tutorial cannot be used in such situations. You need to terminate the main program or disable its automatic startup before restarting the robot.
It's worth noting that because the robot's main demo uses multi-threading and is configured to run automatically at startup through crontab, the usual method sudo killall python typically doesn't work. Therefore, we'll introduce the method of disabling the automatic startup of the main program here.
If you have already disabled the automatic startup of the robot's main demo, you do not need to proceed with the section on Terminate the Main Demo.
Terminate the Main Demo
- 1. Click the "+" icon next to the tab for this page to open a new tab called "Launcher."
- 2. Click on "Terminal" under "Other" to open a terminal window.
- 3. Type bash into the terminal window and press Enter.
- 4. Now you can use the Bash Shell to control the robot.
- 5. Enter the command: crontab -e.
- 6. If prompted to choose an editor, enter 1 and press Enter to select nano.
- 7. After opening the crontab configuration file, you'll see the following two lines:
@reboot ~/ugv_pt_rpi/ugv-env/bin/python ~/ugv_pt_rpi/app.py >> ~/ugv.log 2>&1 @reboot /bin/bash ~/ugv_pt_rpi/start_jupyter.sh >> ~/jupyter_log.log 2>&1
- 8. Add a # character at the beginning of the line with ……app.py >> …… to comment out this line.
#@reboot ~/ugv_pt_rpi/ugv-env/bin/python ~/ugv_pt_rpi/app.py >> ~/ugv.log 2>&1 @reboot /bin/bash ~/ugv_pt_rpi/start_jupyter.sh >> ~/jupyter_log.log 2>&1
- 9. Press Ctrl + X in the terminal window to exit. It will ask you "Save modified buffer?" Enter Y and press Enter to save the changes.
- 10. Reboot the device. Note that this process will temporarily close the current Jupyter Lab session. If you didn't comment out ……start_jupyter.sh >>…… in the previous step, you can still use Jupyter Lab normally after the robot reboots (JupyterLab and the robot's main program app.py run independently). You may need to refresh the page.
- 11. One thing to note is that since the sub-controller continues to communicate with the upper machine through the serial port, the host may not start up properly during the restart process due to the continuous change of serial port levels. Taking the case where the upper machine is a Raspberry Pi, after the Raspberry Pi is shut down and the green LED is constantly on without the green LED blinking, you can turn off the power switch of the robot, then turn it on again, and the robot will restart normally.
- 12. Enter the reboot command: sudo reboot.
- 13. After waiting for the device to restart (during the restart process, the green LED of the Raspberry Pi will blink, and when the frequency of the green LED blinking decreases or goes out, it means that the startup is successful), refresh the page and continue with the remaining part of this tutorial.
Example
The following code block can be run directly:
- 1. Select the code block below.
- 2. Press Shift + Enter to run the code block.
- 3. Watch the real-time video window.
- 4. Press STOP to close the real-time video and release the camera resources.
If you cannot see the real-time camera feed when running:
- Click on Kernel -> Shut down all kernels above.
- Close the current section tab and open it again.
- Click STOP to release the camera resources, then run the code block again.
- Reboot the device.
Notes
If you are using a USB camera, you need to uncomment the line frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB).
Running
When the code block is executed, you can take a photo by clicking on PHOTO.
import cv2 # Import the OpenCV library for image processing from picamera2 import Picamera2 # Import the library to access the Raspberry Pi Camera import numpy as np # Import the library for mathematical calculations from IPython.display import display, Image # Import to display images in Jupyter Notebook import ipywidgets as widgets # Import to create interactive interface widgets like buttons import threading # Import to create new threads for asynchronous task execution import os, time # Import for file and directory operations and time-related functions time_interval = 3 # Set the time interval for taking photos (seconds) photo_path = '/home/ws/ugv_pt_rpi/static/' # Set the directory path to store photos and videos # Create a "Stop" button for users to stop video capture and photo taking # ================ stopButton = widgets.ToggleButton( value=False, description='Stop', disabled=False, button_style='danger', # Set button style: 'success', 'info', 'warning', 'danger' or '' tooltip='Description', icon='square' # Set button icon (FontAwesome names without the `fa-` prefix) ) # Create a "Photo" button for users to instantly take a photo by clicking it # ================ photoButton = widgets.ToggleButton( value=False, description='Photo', disabled=False, button_style='danger', # 'success', 'info', 'warning', 'danger' or '' tooltip='Description', icon='square' # (FontAwesome names without the `fa-` prefix) ) # Set the time interval for continuous shooting (seconds) time_interval = 3 photo_num_count = 0 # Initialize the photo counter capture_lock = threading.Lock() last_photo_time = time.time() # Record the last time a photo was taken def photo_button_clicked(change, frame): global photo_num_count if change['new']: # When the "Photo" button is clicked photo_num_count += 1 # Increment the photo counter photo_filename = f'{photo_path}photo_{photo_num_count}.jpg' # Set the path and filename for saving the photo cv2.imwrite(photo_filename, frame) # Save the photo print(f'{photo_num_count} photos saved. New photo: {photo_filename}') # Print photo save information photoButton.value = False # Reset the status of the "Photo" button # Define a display function to capture and display video frames and respond to photo capture requests # ================ def view(stop_button, photo_button): # If you are using a CSI camera you need to comment out the picam2 code and the camera code # Since the latest versions of OpenCV no longer support CSI cameras (4.9.0.80), you need to use picamera2 to get the camera footage # picam2 = Picamera2() # Create an instance of Picamera2 # picam2.configure(picam2.create_video_configuration(main={"format": 'XRGB8888', "size": (640, 480)})) # Configure camera parameters # picam2.start() camera = cv2.VideoCapture(-1) #Create camera example #Set resolution camera.set(cv2.CAP_PROP_FRAME_WIDTH, 640) camera.set(cv2.CAP_PROP_FRAME_HEIGHT, 480) display_handle = display(None, display_id=True) # Create a display handle to update the displayed image i = 0 while True: #frame = picam2.capture_array() # Capture a frame from the camera _, frame = camera.read() photoButton.observe(lambda change: photo_button_clicked(change, frame), names='value') # Listen for clicks on the "Photo" button _, frame = cv2.imencode('.jpeg', frame) # Encode the frame as JPEG format display_handle.update(Image(data=frame.tobytes())) if stopButton.value: #picam2.close() # If yes, close the camera cv2.release() # if yes, close the camera display_handle.update(None) # Display the "Stop" and "Photo" buttons and start a new thread to execute the display function # ================ display(stopButton) display(photoButton) thread = threading.Thread(target=view, args=(stopButton, photoButton,)) thread.start()
Here's something to note: due to stability issues with JupyterLab components used in this example, pressing the "Photo" button may result in saving multiple photos. You can navigate to ugv_pt_rpi/static/ in the left sidebar of JupyterLab to view the captured photos.