Skip to content

Commit f8ac8e4

Browse files
Add examples for remote.py
Signed-off-by: Peter Bailey <[email protected]>
1 parent 911ab39 commit f8ac8e4

File tree

5 files changed

+253
-0
lines changed

5 files changed

+253
-0
lines changed
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
#!/usr/bin/python3
2+
3+
# This shows how to use the remote module to detect motion in an image.
4+
# It estimates the motion of the image and draws it on the image.
5+
6+
import queue
7+
import threading
8+
9+
import cv2
10+
import numpy as np
11+
12+
import picamera2
13+
from picamera2 import MappedArray, Process, RemoteMappedArray
14+
15+
BLOCK_SIZE = 32
16+
SEARCH_SIZE = 16
17+
STEP_SIZE = 8
18+
19+
last_frame = None
20+
motion_map = None
21+
22+
23+
def run(request):
24+
global last_frame
25+
26+
if last_frame is None:
27+
last_frame = request.make_array("main")
28+
return None
29+
30+
with RemoteMappedArray(request, "main") as m:
31+
motion_map = calculate_motion(last_frame, m.array)
32+
33+
last_frame = request.make_array("main")
34+
35+
return motion_map
36+
37+
38+
def return_thread(futures):
39+
global motion_map
40+
41+
while True:
42+
future = futures.get()
43+
motion_map = future.result()
44+
45+
46+
def calculate_motion(frame1, frame2):
47+
motion_map = np.zeros((frame1.shape[0] // BLOCK_SIZE, frame1.shape[1] // BLOCK_SIZE, 2), dtype=np.int8)
48+
for block_x in range(0, frame1.shape[1], BLOCK_SIZE):
49+
for block_y in range(0, frame1.shape[0], BLOCK_SIZE):
50+
block = frame1[block_y:block_y + BLOCK_SIZE, block_x:block_x + BLOCK_SIZE]
51+
min_diff = np.inf
52+
max_diff = 0
53+
for offset_x in range(-SEARCH_SIZE, SEARCH_SIZE + 1, STEP_SIZE):
54+
if block_x + offset_x < 0 or block_x + offset_x + BLOCK_SIZE >= frame2.shape[1]:
55+
continue
56+
for offset_y in range(-SEARCH_SIZE, SEARCH_SIZE + 1, STEP_SIZE):
57+
if block_y + offset_y < 0 or block_y + offset_y + BLOCK_SIZE >= frame2.shape[0]:
58+
continue
59+
block2 = frame2[block_y + offset_y:block_y + offset_y + BLOCK_SIZE,
60+
block_x + offset_x:block_x + offset_x + BLOCK_SIZE]
61+
diff = np.sum((block - block2)**2)
62+
if diff < min_diff:
63+
min_diff = diff
64+
min_offset = (offset_x, offset_y)
65+
if diff > max_diff:
66+
max_diff = diff
67+
68+
if max_diff < 4 * min_diff:
69+
motion_map[block_y // BLOCK_SIZE, block_x // BLOCK_SIZE] = (0, 0)
70+
else:
71+
motion_map[block_y // BLOCK_SIZE, block_x // BLOCK_SIZE] = min_offset
72+
73+
return motion_map
74+
75+
76+
def draw_motion_map(request):
77+
if motion_map is None:
78+
return
79+
80+
with MappedArray(request, "main") as m:
81+
for block_x in range(0, motion_map.shape[1]):
82+
for block_y in range(0, motion_map.shape[0]):
83+
mid_x = block_x * BLOCK_SIZE + BLOCK_SIZE // 2
84+
mid_y = block_y * BLOCK_SIZE + BLOCK_SIZE // 2
85+
offset_x, offset_y = motion_map[block_y, block_x]
86+
cv2.arrowedLine(m.array, (mid_x, mid_y), (mid_x + offset_x, mid_y + offset_y), (0, 0, 255), 2)
87+
88+
89+
if __name__ == "__main__":
90+
picam2 = picamera2.Picamera2()
91+
config = picam2.create_preview_configuration()
92+
config["buffer_count"] = 2
93+
picam2.configure(config)
94+
picam2.post_callback = draw_motion_map
95+
picam2.start_preview(picamera2.Preview.QTGL)
96+
picam2.start()
97+
98+
process = Process(run, picam2)
99+
100+
futures = queue.Queue()
101+
return_thread = threading.Thread(target=return_thread, args=(futures,))
102+
return_thread.start()
103+
104+
for _ in range(1000):
105+
with picam2.captured_request() as request:
106+
future = process.send(request)
107+
futures.put(future)
108+
109+
return_thread.join()
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
#!/usr/bin/python3
2+
3+
# Shows how to use the remote module to process images in a separate process.
4+
# This is doing a simple sum of the pixels in the image in a separate process.
5+
# The results are then gathered by the main process and printed.
6+
7+
import multiprocessing as mp
8+
import os
9+
import queue
10+
import threading
11+
import time
12+
13+
import numpy as np
14+
15+
import picamera2
16+
from picamera2 import Pool, RemoteMappedArray
17+
18+
IMAGE_COUNT = 100
19+
20+
21+
def run(request):
22+
print(f"Recieved request in process {os.getpid()}")
23+
with RemoteMappedArray(request, "main") as m:
24+
s = np.sum(m.array)
25+
time.sleep(0.1) # long calculation
26+
print(f"Sum of array: {s}")
27+
print(f"Processed request in process {os.getpid()}")
28+
return s
29+
30+
31+
def gather_results(futures):
32+
for i in range(IMAGE_COUNT):
33+
future = futures.get()
34+
print(f"Recieved result {i + 1}: {future.result()}")
35+
36+
37+
if __name__ == "__main__":
38+
mp.set_start_method("spawn")
39+
picam2 = picamera2.Picamera2()
40+
config = picam2.create_preview_configuration()
41+
config["buffer_count"] = 16
42+
picam2.start(config)
43+
44+
with Pool(run, 16, picam2) as pool:
45+
futures = queue.Queue()
46+
results_thread = threading.Thread(target=gather_results, args=(futures,))
47+
results_thread.start()
48+
49+
for _ in range(IMAGE_COUNT):
50+
with picam2.captured_request() as request:
51+
future = pool.send(request)
52+
futures.put(future)
53+
54+
results_thread.join()

examples/remote_processing_pool.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#!/usr/bin/python3
2+
3+
# Shows how the Pool class can be used to process images in separate processes.
4+
# This program creates a pool of 16 processes and sends 100 requests to the pool.
5+
# It keeps track of the number of requests processed by each process and prints the results.
6+
7+
import os
8+
import time
9+
10+
import picamera2
11+
from picamera2 import Pool
12+
13+
14+
def init():
15+
global counter
16+
counter = 0
17+
18+
19+
def run(request):
20+
global counter
21+
counter += 1
22+
time.sleep(0.5)
23+
return (os.getpid(), counter)
24+
25+
26+
if __name__ == "__main__":
27+
picam2 = picamera2.Picamera2()
28+
config = picam2.create_preview_configuration()
29+
config["buffer_count"] = 16
30+
picam2.start(config)
31+
32+
futures = []
33+
34+
with Pool(run, 16, picam2, init) as pool:
35+
for _ in range(100):
36+
with picam2.captured_request() as request:
37+
future = pool.send(request)
38+
futures.append(future)
39+
40+
print("Pool Closed")
41+
42+
counts = {}
43+
counts.update(future.result() for future in futures)
44+
45+
print(counts)

examples/remote_save_image.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
#!/usr/bin/python3
2+
3+
# This shows how to use the remote module to save an image to a file.
4+
# This takes 100 images and saves them to a file in a separate process.
5+
# The images are saved in a folder called images.
6+
# The images are blurred as a simple example of processing the image in a separate process.
7+
# They have the process id that created them in the filename.
8+
9+
import os
10+
11+
import cv2
12+
13+
import picamera2
14+
from picamera2 import Pool, RemoteMappedArray
15+
16+
17+
def init():
18+
global counter
19+
counter = 0
20+
21+
22+
def save_image(request):
23+
global counter
24+
counter += 1
25+
with RemoteMappedArray(request, "main") as m:
26+
temp_image = cv2.medianBlur(m.array, 9)
27+
m.array[:] = temp_image
28+
29+
request.save(f"images_{os.getpid()}_{counter}.jpg")
30+
request.save_dng(f"images_{os.getpid()}_{counter}.dng")
31+
32+
33+
if __name__ == "__main__":
34+
picam2 = picamera2.Picamera2()
35+
config = picam2.create_preview_configuration()
36+
config["buffer_count"] = 8
37+
picam2.start(config)
38+
39+
with Pool(save_image, 8, picam2, init) as pool:
40+
for _ in range(25):
41+
with picam2.captured_request() as request:
42+
pool.send(request)

tests/test_list.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ examples/preview_x_forwarding.py
3636
examples/pyav_capture.py
3737
examples/pyav_circular_capture.py
3838
examples/raw.py
39+
examples/remote_processing_pool.py
40+
examples/remote_processing_example.py
41+
examples/remote_save_image.py
3942
examples/request_context_manager.py
4043
examples/rotation.py
4144
examples/still_capture_with_config.py

0 commit comments

Comments
 (0)