From 2d6ae37fe03dbf22db429097243255d46cc36a9d Mon Sep 17 00:00:00 2001 From: Michael Mikovsky <77305074+Astatin3@users.noreply.github.com> Date: Mon, 17 Mar 2025 14:23:11 -0600 Subject: [PATCH] update --- deploy.sh | 5 + pygame-video.py | 162 ++++++++++++++++++++++++++++ raspberry-pi-logo.png | Bin 0 -> 32269 bytes src/main.py | 240 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 407 insertions(+) create mode 100755 deploy.sh create mode 100644 pygame-video.py create mode 100644 raspberry-pi-logo.png create mode 100644 src/main.py diff --git a/deploy.sh b/deploy.sh new file mode 100755 index 0000000..54b5483 --- /dev/null +++ b/deploy.sh @@ -0,0 +1,5 @@ +sshpass -p Bookshelf scp -r src/ username@10.42.0.1:~/ + +echo "########## Starting #########" + +sshpass -p Bookshelf ssh username@10.42.0.1 "cd src && python3 main.py" diff --git a/pygame-video.py b/pygame-video.py new file mode 100644 index 0000000..c0f4722 --- /dev/null +++ b/pygame-video.py @@ -0,0 +1,162 @@ +from pygame.locals import KEYDOWN, K_ESCAPE, K_q +import pygame +import cv2 +import sys + +size = [400, 300] + +camera = cv2.VideoCapture(0) +pygame.init() +pygame.display.set_caption("OpenCV camera stream on Pygame") +screen = pygame.display.set_mode(size) + +font = pygame.font.SysFont('Arial', 12) + +MAJOR_TICK_LENGTH = 15 +MINOR_TICK_LENGTH = 8 +LABEL_OFFSET = 20 + +WHITE = (255, 255, 255) +GRAY = (128, 128, 128) + + +YAW_HEIGHT = 1 +PITCH_WIDTH = 10 + +class PitchYawHUD: + def __init__(self, screen_width=800, screen_height=600): + # pygame.init() + # screen = pygame.display.set_mode((screen_width, screen_height)) + # pygame.display.set_caption("Pitch & Yaw HUD") + + # Colors + self.WHITE = (255, 255, 255) + self.GRAY = (128, 128, 128) + + # HUD dimensions + self.YAW_HEIGHT = 1 + self.PITCH_WIDTH = 10 + + # Tick marks + self.MAJOR_TICK_LENGTH = 15 + self.MINOR_TICK_LENGTH = 8 + self.LABEL_OFFSET = 20 + + # Font + self.font = pygame.font.SysFont('Arial', 12) + + # Cardinal directions for yaw + self.cardinal_directions = { + 0: "N", 45: "NE", 90: "E", 135: "SE", + 180: "S", 225: "SW", 270: "W", 315: "NW" + } + + def draw_yaw_indicator(self, screen, yaw_angle): + # Normalize yaw angle to 0-360 + yaw_angle = yaw_angle % 360 + + + # Calculate pixel per degree for yaw + pixels_per_degree = screen.get_width() / 360 + + # Draw tick marks + for angle in range(0, 360, 5): # Draw every 5 degrees + x_pos = (angle - yaw_angle) * pixels_per_degree + x_pos = x_pos % screen.get_width() + + # Determine tick length + if angle % 45 == 0: # Cardinal and intercardinal directions + tick_length = self.MAJOR_TICK_LENGTH + # Draw direction label + if angle in self.cardinal_directions: + label = self.font.render(self.cardinal_directions[angle], True, self.WHITE) + screen.blit(label, (x_pos - label.get_width()/2, self.LABEL_OFFSET)) + else: + tick_length = self.MINOR_TICK_LENGTH + + # Draw tick + pygame.draw.line(screen, self.WHITE, + (x_pos, 0), + (x_pos, tick_length)) + + def draw_pitch_indicator(self, screen, pitch_angle): + # Normalize pitch angle to -180 to 180 + pitch_angle = max(-180, min(180, pitch_angle)) + + # Draw background + # pygame.draw.rect(screen, self.GRAY, + # (screen.get_width() - self.PITCH_WIDTH, 0, + # self.PITCH_WIDTH, screen.get_height())) + + # Calculate pixel per degree for pitch + pixels_per_degree = screen.get_height() / 360 + + # Draw tick marks + for angle in range(-180, 181, 10): # Draw every 10 degrees + y_pos = screen.get_height()/2 + (angle - pitch_angle) * pixels_per_degree + + # Skip if outside screen + if y_pos < 0 or y_pos > screen.get_height(): + continue + + # Determine tick length + if angle % 30 == 0: # Major ticks + tick_length = self.MAJOR_TICK_LENGTH + # Draw angle label + label = self.font.render(str(angle), True, self.WHITE) + screen.blit(label, + (screen.get_width() - self.PITCH_WIDTH - label.get_width() - 5, + y_pos - label.get_height()/2)) + else: + tick_length = self.MINOR_TICK_LENGTH + + # Draw tick + pygame.draw.line(screen, self.WHITE, + (screen.get_width() - self.PITCH_WIDTH, y_pos), + (screen.get_width() - self.PITCH_WIDTH + tick_length, y_pos)) + + def update(self, screen, pitch, yaw): + # screen.fill((0, 0, 0)) # Clear screen + self.draw_yaw_indicator(screen, yaw) + self.draw_pitch_indicator(screen, pitch) + pygame.display.flip() + + + +hud = PitchYawHUD() + +pitch = 0 +yaw = 0 + + + +try: + while True: + + ret, frame = camera.read() + + if not ret: continue + + screen.fill([0, 0, 0]) + frame = cv2.resize(frame, dsize=(size[0], size[1]), interpolation=cv2.INTER_CUBIC) + frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) + frame = frame.swapaxes(0, 1) + pygame.surfarray.blit_array(screen, frame) + pygame.display.update() + + hud.update(screen, pitch, yaw) + + pitch += 1 + yaw += 1 + + + for event in pygame.event.get(): + if event.type == pygame.QUIT: + sys.exit(0) + elif event.type == KEYDOWN: + if event.key == K_ESCAPE or event.key == K_q: + sys.exit(0) + +except (KeyboardInterrupt, SystemExit): + pygame.quit() + cv2.destroyAllWindows() \ No newline at end of file diff --git a/raspberry-pi-logo.png b/raspberry-pi-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..0d715c5296bae6e3e26746748ee842c511728af2 GIT binary patch literal 32269 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 literal 0 HcmV?d00001 diff --git a/src/main.py b/src/main.py new file mode 100644 index 0000000..a1afa86 --- /dev/null +++ b/src/main.py @@ -0,0 +1,240 @@ +import numpy as np +import fcntl +import mmap +import struct +import time +import os + +class FrameBuffer: + """Class to handle direct framebuffer access on Raspberry Pi.""" + + def __init__(self, device='/dev/fb0'): + # Open the framebuffer device + self.fb = open(device, 'rb+') + + # Get fixed screen information + fix_info = struct.unpack('IIIIHH', + fcntl.ioctl(self.fb.fileno(), + 0x4602, # FBIOGET_FSCREENINFO + struct.pack('IIIIHH', 0, 0, 0, 0, 0, 0))) + + # Get variable screen information + var_info = struct.unpack('IIIIIIIIHHHHHH', + fcntl.ioctl(self.fb.fileno(), + 0x4600, # FBIOGET_VSCREENINFO + struct.pack('IIIIIIIIHHHHHH', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0))) + + self.xres = var_info[0] + self.yres = var_info[1] + self.bits_per_pixel = var_info[6] + self.bytes_per_pixel = self.bits_per_pixel // 8 + + print(f"Screen size: {self.xres}x{self.yres}") + + # Map the framebuffer to memory + self.fb_size = fix_info[1] # Length of framebuffer memory + self.fb_map = mmap.mmap(self.fb.fileno(), self.fb_size, mmap.MAP_SHARED, mmap.PROT_WRITE|mmap.PROT_READ) + + def display_frame(self, frame): + """Display a numpy array as a frame. + + Args: + frame: numpy array of shape (height, width, 3) with uint8 RGB values + """ + # Ensure frame matches framebuffer dimensions + if frame.shape[:2] != (self.yres, self.xres): + frame = cv2.resize(frame, (self.xres, self.yres)) + + # Convert frame to correct format (BGR888 to RGB565) + if self.bits_per_pixel == 16: + # Convert RGB888 to RGB565 + r = (frame[:, :, 0] >> 3).astype(np.uint16) << 11 + g = (frame[:, :, 1] >> 2).astype(np.uint16) << 5 + b = (frame[:, :, 2] >> 3).astype(np.uint16) + frame_rgb565 = (r | g | b).astype(np.uint16) + buffer = frame_rgb565.tobytes() + else: + # Assume 24/32 bit color + buffer = frame.tobytes() + + # Write to framebuffer + self.fb_map.seek(0) + self.fb_map.write(buffer) + + def __del__(self): + self.fb_map.close() + self.fb.close() + + + + +class PitchYawHUD: + def __init__(self, width=800, height=600): + self.width = width + self.height = height + + # Colors (in BGR for OpenCV) + self.WHITE = (255, 255, 255) + self.GRAY = (128, 128, 128) + self.BLACK = (0, 0, 0) + + # HUD dimensions + self.YAW_HEIGHT = 40 + self.PITCH_WIDTH = 40 + + # Tick marks + self.MAJOR_TICK_LENGTH = 15 + self.MINOR_TICK_LENGTH = 8 + self.LABEL_OFFSET = 20 + + # Font + self.font = cv2.FONT_HERSHEY_SIMPLEX + self.font_scale = 0.4 + + # Cardinal directions for yaw + self.cardinal_directions = { + 0: "N", 45: "NE", 90: "E", 135: "SE", + 180: "S", 225: "SW", 270: "W", 315: "NW" + } + + def draw_text(self, img, text, pos, color=None): + if color is None: + color = self.WHITE + cv2.putText(img, text, pos, self.font, self.font_scale, color, 1, cv2.LINE_AA) + + def draw_yaw_indicator(self, img, yaw_angle): + # Normalize yaw angle to 0-360 + yaw_angle = yaw_angle % 360 + + # Draw background + # cv2.rectangle(img, (0, 0), (self.width, self.YAW_HEIGHT), self.GRAY, -1) + + # Calculate pixel per degree for yaw + pixels_per_degree = self.width / 360 + + # Draw tick marks + for angle in range(0, 360, 5): # Draw every 5 degrees + x_pos = int((angle - yaw_angle) * pixels_per_degree) + x_pos = x_pos % self.width + + # Determine tick length + if angle % 45 == 0: # Cardinal and intercardinal directions + tick_length = self.MAJOR_TICK_LENGTH + # Draw direction label + if angle in self.cardinal_directions: + text = self.cardinal_directions[angle] + text_size = cv2.getTextSize(text, self.font, self.font_scale, 1)[0] + text_x = int(x_pos - text_size[0]/2) + self.draw_text(img, text, (text_x, self.LABEL_OFFSET + text_size[1])) + else: + tick_length = self.MINOR_TICK_LENGTH + + # Draw tick + cv2.line(img, (x_pos, 0), (x_pos, tick_length), self.WHITE, 1, cv2.LINE_AA) + + def draw_pitch_indicator(self, img, pitch_angle): + # Normalize pitch angle to -180 to 180 + pitch_angle = max(-180, min(180, pitch_angle)) + + # Draw background + # cv2.rectangle(img, + # (self.width - self.PITCH_WIDTH, 0), + # (self.width, self.height), + # self.GRAY, -1) + + # Calculate pixel per degree for pitch + pixels_per_degree = self.height / 360 + + # Draw tick marks + for angle in range(-180, 181, 10): # Draw every 10 degrees + y_pos = int(self.height/2 + (angle - pitch_angle) * pixels_per_degree) + + # Skip if outside screen + if y_pos < 0 or y_pos > self.height: + continue + + # Determine tick length + if angle % 30 == 0: # Major ticks + tick_length = self.MAJOR_TICK_LENGTH + # Draw angle label + text = str(angle) + text_size = cv2.getTextSize(text, self.font, self.font_scale, 1)[0] + text_x = self.width - self.PITCH_WIDTH - text_size[0] - 5 + text_y = int(y_pos + text_size[1]/2) + self.draw_text(img, text, (text_x, text_y)) + else: + tick_length = self.MINOR_TICK_LENGTH + + # Draw tick + start_point = (self.width - self.PITCH_WIDTH, y_pos) + end_point = (self.width - self.PITCH_WIDTH + tick_length, y_pos) + cv2.line(img, start_point, end_point, self.WHITE, 1, cv2.LINE_AA) + + def update(self, img, pitch, yaw): + # Create blank image + # img = np.zeros((self.height, self.width, 3), dtype=np.uint8) + + # Draw indicators + self.draw_yaw_indicator(img, yaw) + self.draw_pitch_indicator(img, pitch) + + return img + + + + + +# Example usage +if __name__ == "__main__": + print("Importing...") + import cv2 + import time + + # Initialize framebuffer + print("Framebuffer...") + fb = FrameBuffer() + + hud = PitchYawHUD(width=fb.xres, height=fb.yres) + + # Create a test pattern + print("Creating test pattern...") + test_frame = np.zeros((fb.yres, fb.xres, 3), dtype=np.uint8) + + cap = cv2.VideoCapture(0) + + if not cap.isOpened(): + print("Error: Could not open camera.") + exit() + + + + cap.set(cv2.CAP_PROP_FRAME_WIDTH, fb.xres) + cap.set(cv2.CAP_PROP_FRAME_HEIGHT, fb.yres) + time.sleep(2) + + + # cap.set(cv2.CV_CAP_PROP_FRAME_WIDTH, fb.xres) + # cap.set(cv2.CV_CAP_PROP_FRAME_HEIGHT, fb.yres) + # cap.set(cv2.CV_CAP_PROP_EXPOSURE, 0.1) + + while True: + ret, frame = cap.read() + if not ret: + # print("err") + continue + + print(frame.shape) + + # frame = frame[:fb.xres, :fb.yres] + frame = cv2.resize(frame, dsize=(fb.xres, fb.yres), interpolation=cv2.INTER_CUBIC) + frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) + + # test_frame[y_offset:y_offset+frame.shape[0], x_offset:x_offset+frame.shape[1]] = frame + + frame = hud.update(frame, 0, 0) + + # fb.display_frame(frame) + cv2.imshow("e", frame) + cv2.waitKey(1) + +# convert "/usr/share/rpd-wallpaper/raspberry-pi-logo.png"\["$fbw"x"$fbh"^\] +flip -strip -define bmp:subtype=RGB565 bmp2:- | tail -c $(( fbw * fbh * fbd / 8 )) > /dev/fb0 \ No newline at end of file