From dd1b5854a54261ff1c995431cc5ac9b8d0c2d4ac Mon Sep 17 00:00:00 2001 From: sam Date: Thu, 22 Feb 2024 18:45:09 +0000 Subject: [PATCH] Blackened --- .github/workflows/lint.yml | 8 ++++- README.md | 4 +-- backends/custom/custom_backend.py | 5 +++- backends/fadecandy/opc.py | 2 ++ backends/lcm/lcm_backend.py | 6 ++-- backends/test_backend.py | 15 +++++++--- backends/wled/wled_backend.py | 21 +++++++++---- lib/utils.py | 2 +- scripts/camera_check.py | 21 +++++++++---- scripts/capture_sequence.py | 49 ++++++++++++++++++++++--------- scripts/latency_check.py | 35 ++++++++++++++++------ scripts/reconstruct.py | 27 ++++++++++++----- scripts/visualise.py | 29 ++++++++++++++---- test/test_led_identifier.py | 3 +- 14 files changed, 168 insertions(+), 59 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 06e99b3..a83d505 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -24,6 +24,8 @@ jobs: run: | python -m pip install --upgrade pip pip install flake8 + pip install flake8-bugbear + pip install black - name: Flake 8 Syntax Errors run: | @@ -31,4 +33,8 @@ jobs: - name: Flake 8 Syntax Warnings run: | - flake8 . --count --statistics \ No newline at end of file + flake8 . --count --statistics + + - name: Black formatting + run: | + black . --check \ No newline at end of file diff --git a/README.md b/README.md index 1016866..6b5ac30 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![Windows](https://github.com/TheMariday/L3D/actions/workflows/test_windows.yml/badge.svg)](https://github.com/TheMariday/L3D/actions/workflows/test_windows.yml) [![Ubuntu](https://github.com/TheMariday/L3D/actions/workflows/test_ubuntu.yml/badge.svg)](https://github.com/TheMariday/L3D/actions/workflows/test_ubuntu.yml) [![MacOS](https://github.com/TheMariday/L3D/actions/workflows/test_mac.yml/badge.svg)](https://github.com/TheMariday/L3D/actions/workflows/test_mac.yml) -[![Style](https://github.com/TheMariday/L3D/actions/workflows/lint.yml/badge.svg)](https://github.com/TheMariday/L3D/actions/workflows/lint.yml) +[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) This is a selection of tools to map LEDs into 2D and 3D space using only your webcam! @@ -96,4 +96,4 @@ I would really love to hear what you think and if you have any bugs or improveme If you implement a backend that you think others might use, please raise a pull request or just drop me a message on Telegram! -Please install and run flake8 before submitting changes thank you! \ No newline at end of file +If you want a super speed PR, run flake8, flake8-bugbear and black before submitting changes! \ No newline at end of file diff --git a/backends/custom/custom_backend.py b/backends/custom/custom_backend.py index a654764..381efdb 100644 --- a/backends/custom/custom_backend.py +++ b/backends/custom/custom_backend.py @@ -1,10 +1,13 @@ # import some_led_library + class Backend: def __init__(self, led_count: int): # Remove the following line after you have implemented the set_led function! - raise NotImplementedError("Could not load backend as it has not been implemented, go implement it!") + raise NotImplementedError( + "Could not load backend as it has not been implemented, go implement it!" + ) def set_led(self, led_index: int, on: bool): # Write your code for controlling your LEDs here diff --git a/backends/fadecandy/opc.py b/backends/fadecandy/opc.py index 94b438c..ea9d359 100644 --- a/backends/fadecandy/opc.py +++ b/backends/fadecandy/opc.py @@ -1,4 +1,5 @@ #!/usr/bin/env python +# fmt: off """Python Client library for Open Pixel Control http://github.com/zestyping/openpixelcontrol @@ -211,3 +212,4 @@ def set_interpolation(self, enabled = True): return True +# fmt: on diff --git a/backends/lcm/lcm_backend.py b/backends/lcm/lcm_backend.py index f5e37bc..1f64968 100644 --- a/backends/lcm/lcm_backend.py +++ b/backends/lcm/lcm_backend.py @@ -1,9 +1,9 @@ - - class Backend: def __init__(self, led_count): - raise NotImplementedError("Could not load backend as it has not been implemented, go implement it!") + raise NotImplementedError( + "Could not load backend as it has not been implemented, go implement it!" + ) def set_led(self, led_index: int, on: bool): pass diff --git a/backends/test_backend.py b/backends/test_backend.py index 45c5700..7e8f55c 100644 --- a/backends/test_backend.py +++ b/backends/test_backend.py @@ -1,18 +1,25 @@ import argparse import sys -sys.path.append('./') + +sys.path.append("./") from lib import utils from lib.color_print import cprint import time if __name__ == "__main__": - parser = argparse.ArgumentParser(description='Tests a particular backend by making a reference led blink') + parser = argparse.ArgumentParser( + description="Tests a particular backend by making a reference led blink" + ) utils.add_backend_args(parser) - parser.add_argument("--reference_led", type=int, - help="This is the index of the LED should be visible from the camera", default=0) + parser.add_argument( + "--reference_led", + type=int, + help="This is the index of the LED should be visible from the camera", + default=0, + ) args = parser.parse_args() diff --git a/backends/wled/wled_backend.py b/backends/wled/wled_backend.py index 245be95..ccd91a2 100644 --- a/backends/wled/wled_backend.py +++ b/backends/wled/wled_backend.py @@ -15,22 +15,31 @@ def get_led_count(self): # Get the LED Count straight from the WLED Device :D if response.status_code != 200: - raise ConnectionError(f"Failed to retrieve LED count. Status code: {response.status_code}") + raise ConnectionError( + f"Failed to retrieve LED count. Status code: {response.status_code}" + ) info_data = response.json() - return info_data['leds']['count'] + return info_data["leds"]["count"] def reset_wled(self): # Set all the LED's to black on launch - payload = {"seg": [{"start": 0, "stop": self.get_led_count(), "sel": True}, {"stop": 0}]} + payload = { + "seg": [ + {"start": 0, "stop": self.get_led_count(), "sel": True}, + {"stop": 0}, + ] + } # Send the HTTP POST request to WLED API response = requests.post(self.state_endpoint, json=payload) # Check if the request was successful (HTTP status code 200) if response.status_code != 200: - raise ConnectionError(f"Failed to retrieve LED count. Status code: {response.status_code}") + raise ConnectionError( + f"Failed to retrieve LED count. Status code: {response.status_code}" + ) [self.set_led(i, False) for i in range(self.get_led_count())] def set_led(self, led_index: int, on: bool): @@ -42,4 +51,6 @@ def set_led(self, led_index: int, on: bool): # Check if the request was successful (HTTP status code 200) if response.status_code != 200: - raise ConnectionError(f"WLED Backend failed to set LED state. Status code: {response.status_code}") + raise ConnectionError( + f"WLED Backend failed to set LED state. Status code: {response.status_code}" + ) diff --git a/lib/utils.py b/lib/utils.py index c1a6d3f..47de93f 100644 --- a/lib/utils.py +++ b/lib/utils.py @@ -49,7 +49,7 @@ def get_backend(backend_name, led_count, server=""): from backends.lcm import lcm_backend return lcm_backend.Backend(led_count) - raise "Invalid backend name" + raise RuntimeError("Invalid backend name") except NotImplementedError: cprint(f"Failed to initialise backend {backend_name}, you need to implement it or use the " diff --git a/scripts/camera_check.py b/scripts/camera_check.py index 4870ec8..09878ed 100644 --- a/scripts/camera_check.py +++ b/scripts/camera_check.py @@ -1,24 +1,35 @@ import argparse import sys -sys.path.append('./') + +sys.path.append("./") from lib.utils import add_camera_args from lib import L3D from lib.color_print import cprint, Col if __name__ == "__main__": - parser = argparse.ArgumentParser(description='Tests your webcam and LED detection algorithms') + parser = argparse.ArgumentParser( + description="Tests your webcam and LED detection algorithms" + ) add_camera_args(parser) args = parser.parse_args() if args.width * args.height < 0: - cprint("Failed to start camera checker as both camera width and height need to be provided", format=Col.FAIL) + cprint( + "Failed to start camera checker as both camera width and height need to be provided", + format=Col.FAIL, + ) quit() - l3d = L3D.L3D(args.device, args.exposure, args.threshold, width=args.width, height=args.height) + l3d = L3D.L3D( + args.device, args.exposure, args.threshold, width=args.width, height=args.height + ) - cprint("Camera connected! Hold an LED up to the camera to check LED identification", format=Col.BLUE) + cprint( + "Camera connected! Hold an LED up to the camera to check LED identification", + format=Col.BLUE, + ) l3d.show_debug() diff --git a/scripts/capture_sequence.py b/scripts/capture_sequence.py index c7df8b0..d3448ba 100644 --- a/scripts/capture_sequence.py +++ b/scripts/capture_sequence.py @@ -5,32 +5,44 @@ import cv2 from tqdm import tqdm -sys.path.append('./') + +sys.path.append("./") from lib import utils from lib import L3D from lib.color_print import cprint, Col if __name__ == "__main__": - parser = argparse.ArgumentParser(description='Captures LED flashes to file') + parser = argparse.ArgumentParser(description="Captures LED flashes to file") utils.add_camera_args(parser) utils.add_backend_args(parser) - parser.add_argument("--output_dir", type=str, help="The output folder for your capture", required=True) + parser.add_argument( + "--output_dir", + type=str, + help="The output folder for your capture", + required=True, + ) - parser.add_argument("--led_count", type=int, - help="How many LEDs are in your system", required=True) + parser.add_argument( + "--led_count", type=int, help="How many LEDs are in your system", required=True + ) - parser.add_argument("--latency", type=int, - help="The expected latency in ms from an LED being updated to that being updated in the camera", - default=1000) + parser.add_argument( + "--latency", + type=int, + help="The expected latency in ms from an LED being updated to that being updated in the camera", + default=1000, + ) args = parser.parse_args() led_backend = utils.get_backend(args.backend, args.led_count, args.server) - l3d = L3D.L3D(args.device, args.exposure, args.threshold, width=args.width, height=args.height) + l3d = L3D.L3D( + args.device, args.exposure, args.threshold, width=args.width, height=args.height + ) output_dir_full = os.path.join(os.getcwd(), "my_scans", args.output_dir) @@ -40,16 +52,23 @@ # The filename is made out of the date, then the resolution of the camera string_time = time.strftime("%Y%m%d-%H%M%S") - filename = f"capture_{string_time}_{l3d.cam.get_width()}_{l3d.cam.get_height()}.csv" + filename = ( + f"capture_{string_time}_{l3d.cam.get_width()}_{l3d.cam.get_height()}.csv" + ) filepath = os.path.join(output_dir_full, filename) cprint(f"Opening scan file {filepath}\n") - with open(filepath, 'a') as output_file: + with open(filepath, "a") as output_file: total_leds_found = 0 - for led_id in tqdm(range(args.led_count), unit="LEDs", desc=f"Capturing sequence to {filename}", - total=args.led_count, smoothing=0): + for led_id in tqdm( + range(args.led_count), + unit="LEDs", + desc=f"Capturing sequence to {filename}", + total=args.led_count, + smoothing=0, + ): led_backend.set_led(led_id, True) @@ -60,7 +79,9 @@ result = l3d.find_led(True) if result: # Then no led is found! next - output_file.write(f"{led_id},{result.center[0]},{result.center[1]}\n") + output_file.write( + f"{led_id},{result.center[0]},{result.center[1]}\n" + ) total_leds_found += 1 led_backend.set_led(led_id, False) diff --git a/scripts/latency_check.py b/scripts/latency_check.py index 8a44a99..7906823 100644 --- a/scripts/latency_check.py +++ b/scripts/latency_check.py @@ -1,7 +1,8 @@ import argparse import math import sys -sys.path.append('./') + +sys.path.append("./") from lib import utils from lib import L3D from lib.color_print import cprint, Col @@ -11,10 +12,16 @@ if __name__ == "__main__": - parser = argparse.ArgumentParser(description='Tests the functionality and latency of your LED backend') + parser = argparse.ArgumentParser( + description="Tests the functionality and latency of your LED backend" + ) - parser.add_argument("--reference_led", type=int, - help="This is the index of the LED should be visible from the camera", default=0) + parser.add_argument( + "--reference_led", + type=int, + help="This is the index of the LED should be visible from the camera", + default=0, + ) utils.add_camera_args(parser) utils.add_backend_args(parser) @@ -29,20 +36,27 @@ led_backend.set_led(args.reference_led, False) - l3d = L3D.L3D(args.device, args.exposure, args.threshold, width=args.width, height=args.height) + l3d = L3D.L3D( + args.device, args.exposure, args.threshold, width=args.width, height=args.height + ) # wait for 1 seconds for the backend to update, we don't know the latency at this point time.sleep(1) result = l3d.find_led() if result is not None: - cprint(f"All LEDs should be off, however LED has been detected at {result.center}," - f" please run camera_check to ensure the detector is working properly", Col.FAIL) + cprint( + f"All LEDs should be off, however LED has been detected at {result.center}," + f" please run camera_check to ensure the detector is working properly", + Col.FAIL, + ) quit() latencies = [] - for _ in tqdm(range(100), unit="Tests", desc="Testing average latency", total=100, smoothing=0): + for _ in tqdm( + range(100), unit="Tests", desc="Testing average latency", total=100, smoothing=0 + ): # Set reference led to off and spin until L3D can't find the led anymore led_backend.set_led(args.reference_led, False) while l3d.find_led() is not None: @@ -72,4 +86,7 @@ suggested_latency = round((np.percentile(latencies, 99) * 1.1) * 1000) - cprint(f"Suggested latency value for 99% of cases + 10%: {suggested_latency}ms", Col.BLUE) + cprint( + f"Suggested latency value for 99% of cases + 10%: {suggested_latency}ms", + Col.BLUE, + ) diff --git a/scripts/reconstruct.py b/scripts/reconstruct.py index d54863f..44772b9 100644 --- a/scripts/reconstruct.py +++ b/scripts/reconstruct.py @@ -5,15 +5,22 @@ if __name__ == "__main__": - raise NotImplementedError("This hasn't been properly implemented yet and is still a work in progress") + raise NotImplementedError( + "This hasn't been properly implemented yet and is still a work in progress" + ) logging.basicConfig(level=logging.INFO) parser = argparse.ArgumentParser( - description='Reconstructs 3D information from LED flashes captured with capture_sequence.py') + description="Reconstructs 3D information from LED flashes captured with capture_sequence.py" + ) - parser.add_argument("--input_dir", type=str, - help="Enter the input directory of CSV files", required=True) + parser.add_argument( + "--input_dir", + type=str, + help="Enter the input directory of CSV files", + required=True, + ) args = parser.parse_args() @@ -33,17 +40,23 @@ # Check resolution - file_resolution = [int(r) for r in filename.replace(".csv", "").split("_")[-2:]] # don't ask + file_resolution = [ + int(r) for r in filename.replace(".csv", "").split("_")[-2:] + ] # don't ask if resolution is None: resolution = file_resolution if file_resolution != file_resolution: - logging.error(f"Failed to load file {filename} as resolution does not match other resolutions in this file") + logging.error( + f"Failed to load file {filename} as resolution does not match other resolutions in this file" + ) logging.info(f"Loading file {filepath} with resolution {file_resolution}") with open(filepath, "r") as file: lines = file.readlines() - file_points = [[int(v) for v in line.strip().split(",")] for line in lines] # I really need to go to bed + file_points = [ + [int(v) for v in line.strip().split(",")] for line in lines + ] # I really need to go to bed points.append(file_points) diff --git a/scripts/visualise.py b/scripts/visualise.py index c5718fe..2899212 100644 --- a/scripts/visualise.py +++ b/scripts/visualise.py @@ -4,29 +4,46 @@ if __name__ == "__main__": - parser = argparse.ArgumentParser(description='Visualises 2D and 3D maps') + parser = argparse.ArgumentParser(description="Visualises 2D and 3D maps") - parser.add_argument("filename", type=str, help="The file to visualise, currently only supports 2D mapping") + parser.add_argument( + "filename", + type=str, + help="The file to visualise, currently only supports 2D mapping", + ) args = parser.parse_args() - file_resolution = [int(r) for r in args.filename.replace(".csv", "").split("_")[-2:]] + file_resolution = [ + int(r) for r in args.filename.replace(".csv", "").split("_")[-2:] + ] display = np.zeros((file_resolution[1], file_resolution[0], 3)) with open(args.filename, "r") as file: lines = file.readlines() - file_points = [[int(float(v)) for v in line.strip().split(",")] for line in lines] + file_points = [ + [int(float(v)) for v in line.strip().split(",")] for line in lines + ] if len(file_points[0]) == 3: for point_id, point_u, point_v in file_points: image_point = (int(point_u), int(point_v)) cv2.drawMarker(display, image_point, color=(0, 255, 0)) - cv2.putText(display, f"{point_id}", image_point, cv2.FONT_HERSHEY_SIMPLEX, 1, color=(0, 0, 255)) + cv2.putText( + display, + f"{point_id}", + image_point, + cv2.FONT_HERSHEY_SIMPLEX, + 1, + color=(0, 0, 255), + ) cv2.imshow("Visualise", display) cv2.waitKey(0) if len(file_points[0]) == 4: - raise NotImplementedError("3D point cloud visualisation isn't implemented yet") + raise NotImplementedError( + "3D point cloud visualisation isn't implemented yet" + ) diff --git a/test/test_led_identifier.py b/test/test_led_identifier.py index a1099a8..5335356 100644 --- a/test/test_led_identifier.py +++ b/test/test_led_identifier.py @@ -1,5 +1,6 @@ import sys -sys.path.append('./') + +sys.path.append("./") from lib.led_identifier import LedFinder from mock_camera import MockCamera