Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cedar solve #134

Merged
merged 17 commits into from
Feb 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitmodules
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[submodule "python/PiFinder/tetra3"]
path = python/PiFinder/tetra3
url = https://github.com/esa/tetra3.git
url = https://github.com/smroid/cedar-solve
branch = no_big_files
Binary file added bin/cedar-detect-server-aarch64
Binary file not shown.
Binary file added bin/cedar-detect-server-arm64
Binary file not shown.
2 changes: 1 addition & 1 deletion python/PiFinder/camera_debug.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def __init__(self, exposure_time) -> None:
self.path = utils.pifinder_dir / "test_images"
self.exposure_time = exposure_time
self.gain = 10
self.image = Image.open(self.path / "pifinder_debug.png")
self.image = Image.open(self.path / "debug.png")
self.initialize()

def initialize(self) -> None:
Expand Down
2 changes: 0 additions & 2 deletions python/PiFinder/catalogs.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
import pytz
from typing import List, Dict, DefaultDict, Optional
import numpy as np
import pandas as pd
from collections import defaultdict
from sklearn.neighbors import BallTree

import PiFinder.calc_utils as calc_utils
Expand Down
12 changes: 8 additions & 4 deletions python/PiFinder/integrator.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ def imu_moved(imu_a, imu_b):
Compares two IMU states to determine if they are the 'same'
if either is none, returns False
"""
if imu_a == None:
if imu_a is None:
return False
if imu_b == None:
if imu_b is None:
return False

# figure out the abs difference
Expand All @@ -37,8 +37,13 @@ def imu_moved(imu_a, imu_b):
return False


def integrator(shared_state, solver_queue, console_queue):
def integrator(shared_state, solver_queue, console_queue, is_debug=False):
try:
logger = logging.getLogger()
if is_debug:
logger.setLevel(logging.DEBUG)
logging.debug("Starting Integrator")

solved = {
"RA": None,
"Dec": None,
Expand Down Expand Up @@ -74,7 +79,6 @@ def integrator(shared_state, solver_queue, console_queue):
next_image_solve = None
try:
next_image_solve = solver_queue.get(block=False)
logging.debug("Next image solve is %s", next_image_solve)
except queue.Empty:
pass

Expand Down
15 changes: 8 additions & 7 deletions python/PiFinder/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ def wake_screen(screen_brightness, shared_state, cfg) -> int:
return orig_power_state


def main(script_name=None, show_fps=False):
def main(script_name=None, show_fps=False, verbose=False):
"""
Get this show on the road!
"""
Expand All @@ -213,8 +213,8 @@ def main(script_name=None, show_fps=False):
# Instantiate base keyboard class for keycode
keyboard_base = keyboard_interface.KeyboardInterface()

# Set path for test images
root_dir = os.path.realpath(os.path.join(os.path.dirname(__file__), "..", ".."))
os_detail, platform, arch = utils.get_os_info()
logging.info(f"PiFinder running on {os_detail}, {platform}, {arch}")

# init queues
console_queue = Queue()
Expand Down Expand Up @@ -247,6 +247,7 @@ def main(script_name=None, show_fps=False):
ui_state.set_hint_timeout(cfg.get_option("hint_timeout"))
ui_state.set_active_list_to_history_list()
shared_state.set_ui_state(ui_state)
shared_state.set_arch(arch) # Normal
logging.debug("Ui state in main is" + str(shared_state.ui_state()))
console = UIConsole(display_device, None, shared_state, command_queues, cfg)
console.write("Starting....")
Expand Down Expand Up @@ -284,7 +285,7 @@ def main(script_name=None, show_fps=False):

server_process = Process(
target=server.run_server,
args=(keyboard_queue, gps_queue, shared_state),
args=(keyboard_queue, gps_queue, shared_state, verbose),
)
server_process.start()

Expand Down Expand Up @@ -326,7 +327,7 @@ def main(script_name=None, show_fps=False):
console.update()
solver_process = Process(
target=solver.solver,
args=(shared_state, solver_queue, camera_image, console_queue),
args=(shared_state, solver_queue, camera_image, console_queue, verbose),
)
solver_process.start()

Expand All @@ -335,7 +336,7 @@ def main(script_name=None, show_fps=False):
console.update()
integrator_process = Process(
target=integrator.integrator,
args=(shared_state, solver_queue, console_queue),
args=(shared_state, solver_queue, console_queue, verbose),
)
integrator_process.start()

Expand Down Expand Up @@ -824,4 +825,4 @@ def main(script_name=None, show_fps=False):
fh.setLevel(logger.level)
logger.addHandler(fh)

main(args.script, args.fps)
main(args.script, args.fps, args.verbose)
4 changes: 2 additions & 2 deletions python/PiFinder/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -454,5 +454,5 @@ def update_gps(self):
self.altitude = None


def run_server(q, gps_q, shared_state):
Server(q, gps_q, shared_state)
def run_server(q, gps_q, shared_state, verbose=False):
Server(q, gps_q, shared_state, verbose)
81 changes: 67 additions & 14 deletions python/PiFinder/solver.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,30 @@
* If solved, emits solution into queue

"""
import numpy as np
import time
import logging
import sys
from time import perf_counter as precision_timestamp

from PiFinder.tetra3 import Tetra3
import cedar_detect_client

import PiFinder.tetra3
from PiFinder import utils

sys.path.append(str(utils.tetra3_dir))

# Select method used for star detection and centroiding. True for cedar-detect,
# False for Tetra3.
USE_CEDAR_DETECT = True


def solver(shared_state, solver_queue, camera_image, console_queue):
def solver(shared_state, solver_queue, camera_image, console_queue, is_debug=False):
logging.getLogger("tetra3.Tetra3").addHandler(logging.NullHandler())
t3 = Tetra3(utils.astro_data_dir / "pifinder_fov10-5_m7_hip.npz")
logging.debug("Starting Solver")
t3 = PiFinder.tetra3.tetra3.Tetra3(
str(utils.cwd_dir / "PiFinder/tetra3/tetra3/data/default_database.npz")
)
last_solve_time = 0
solved = {
"RA": None,
Expand All @@ -25,35 +39,74 @@ def solver(shared_state, solver_queue, camera_image, console_queue):
"solve_time": None,
"cam_solve_time": 0,
}

if USE_CEDAR_DETECT:
cedar_detect = cedar_detect_client.CedarDetectClient(
binary_path=str(utils.cwd_dir / "../bin/cedar-detect-server-")
+ shared_state.arch()
)
try:
while True:
if shared_state.power_state() <= 0:
time.sleep(0.5)
# use the time the exposure started here to
# reject images startede before the last solve
# reject images started before the last solve
# which might be from the IMU
last_image_metadata = shared_state.last_image_metadata()
if (
last_image_metadata["exposure_end"] > (last_solve_time)
and last_image_metadata["imu_delta"] < 0.1
):
solve_image = camera_image.copy()
img = camera_image.copy()
img = img.convert(mode="L")
np_image = np.asarray(img, dtype=np.uint8)

new_solve = t3.solve_from_image(
solve_image,
fov_estimate=10.2,
fov_max_error=0.5,
solve_timeout=500,
target_pixel=shared_state.solve_pixel(),
t0 = precision_timestamp()
if USE_CEDAR_DETECT:
centroids = cedar_detect.extract_centroids(
np_image, sigma=8, max_size=8, use_binned=True
)
else:
logging.info("Falling back to Tetra3 for centroiding")
centroids = tetra3.get_centroids_from_image(np_image)
t_extract = (precision_timestamp() - t0) * 1000
logging.debug(
"File %s, extracted %d centroids in %.2fms"
% ("camera", len(centroids), t_extract)
)

solved |= new_solve
if len(centroids) == 0:
logging.debug("No stars found, skipping")
return
else:
solution = t3.solve_from_centroids(
centroids,
(512, 512),
fov_estimate=10.2,
fov_max_error=1.0,
match_max_error=0.005,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to leave the fov_max_error parameter in here, but it can go to 1.0 rather than 0.5.

return_matches=True,
target_pixel=shared_state.solve_pixel(),
solve_timeout=1000,
)

if "matched_centroids" in solution:
# Don't clutter printed solution with these fields.
# del solution['matched_centroids']
# del solution['matched_stars']
del solution["matched_catID"]
del solution["pattern_centroids"]
del solution["epoch_equinox"]
del solution["epoch_proper_motion"]
del solution["cache_hit_fraction"]

solved |= solution

total_tetra_time = solved["T_extract"] + solved["T_solve"]
total_tetra_time = t_extract + solved["T_solve"]
if total_tetra_time > 1000:
console_queue.put(f"SLV: Long: {total_tetra_time}")

if solved["RA"] != None:
if solved["RA"] is not None:
# map the RA/DEC to the target pixel RA/DEC
solved["RA"] = solved["RA_target"]
solved["Dec"] = solved["Dec_target"]
Expand Down
7 changes: 7 additions & 0 deletions python/PiFinder/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ def __init__(self):
self.__datetime_time = None
self.__screen = None
self.__solve_pixel = config.Config().get_option("solve_pixel")
self.__arch = None

def serialize(self, output_file):
with open(output_file, "wb") as f:
Expand All @@ -169,6 +170,12 @@ def power_state(self):
def set_power_state(self, v):
self.__power_state = v

def arch(self):
return self.__arch

def set_arch(self, v):
self.__arch = v

def solve_state(self):
return self.__solve_state

Expand Down
2 changes: 1 addition & 1 deletion python/PiFinder/tetra3
Submodule tetra3 updated 43 files
+9 −4 .gitignore
+14 −0 .readthedocs.yaml
+1 −1 LICENSE.txt
+9 −33 README.rst
+1 −5 __init__.py
+38 −48 docs/installation.rst
+2 −0 docs/requirements.txt
+6 −0 examples/data/credits.txt
+ examples/data/medium_fov/2019-07-29T204726_Alt40_Azi-135_Try1.jpg
+ examples/data/medium_fov/2019-07-29T204726_Alt40_Azi-45_Try1.jpg
+ examples/data/medium_fov/2019-07-29T204726_Alt40_Azi135_Try1.jpg
+ examples/data/medium_fov/2019-07-29T204726_Alt40_Azi45_Try1.jpg
+ examples/data/medium_fov/2019-07-29T204726_Alt60_Azi-135_Try1.jpg
+ examples/data/medium_fov/2019-07-29T204726_Alt60_Azi-45_Try1.jpg
+ examples/data/medium_fov/2019-07-29T204726_Alt60_Azi135_Try1.jpg
+ examples/data/medium_fov/2019-07-29T204726_Alt60_Azi45_Try1.jpg
+ examples/data/medium_fov/crappy.jpg
+ examples/data/medium_fov/hale_bopp.jpg
+ examples/data/medium_fov/jan1_g90_e100ms.jpg
+ examples/data/medium_fov/jan1_g90_e50ms.jpg
+ examples/data/medium_fov/orion2.jpg
+ examples/data/medium_fov/orion_belt.jpg
+ examples/data/medium_fov/orion_trees.jpg
+ examples/data/medium_fov/pleiades.jpg
+ examples/data/medium_fov/roof.jpg
+ examples/data/medium_fov/test2_g100_e50ms.jpg
+ examples/data/medium_fov/test_5mp_g100_e50ms.jpg
+ examples/data/medium_fov/tree.jpg
+16 −0 examples/generate_database.py
+95 −13 examples/test_tetra3.py
+20 −0 setup.py
+0 −1,681 tetra3.py
+5 −0 tetra3/__init__.py
+5 −0 tetra3/bin/README.txt
+ tetra3/bin/cedar-detect-server
+32 −0 tetra3/breadth_first_combinations.py
+112 −0 tetra3/cedar_detect_client.py
+36 −0 tetra3/cedar_detect_pb2.py
+65 −0 tetra3/cedar_detect_pb2.pyi
+66 −0 tetra3/cedar_detect_pb2_grpc.py
+ tetra3/data/default_database.npz
+105 −0 tetra3/proto/cedar_detect.proto
+2,676 −0 tetra3/tetra3.py
36 changes: 17 additions & 19 deletions python/PiFinder/ui/preview.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,26 @@
This module contains all the UI Module classes

"""
import uuid
import os
import time
from PIL import Image, ImageDraw, ImageFont, ImageChops, ImageOps
from PiFinder.ui.fonts import Fonts as fonts
from PiFinder import tetra3
from numpy import ndarray

from PiFinder.ui.base import UIModule
from PiFinder.image_util import (
gamma_correct_high,
gamma_correct_med,
gamma_correct_low,
subtract_background,
)
from PiFinder.ui.base import UIModule
import numpy as np
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to change this now, but it reminded me to tell you that I'm planning to implement isort linting of imports at some point. I'll do a full pass on the code base and set up a pre-commit to just run and modify the files:
https://pypi.org/project/isort/

I'll probably do this along with getting mypy up and running with very loose checking initially and then tightening it down as we update the code.

import time
from PIL import Image, ImageChops, ImageOps
from PiFinder.ui.fonts import Fonts as fonts
from PiFinder import utils
import sys

sys.path.append(str(utils.tetra3_dir))


class UIPreview(UIModule):
from PiFinder import tetra3

__title__ = "CAMERA"
__button_hints__ = {
"B": "Align",
Expand Down Expand Up @@ -79,7 +81,7 @@ def __init__(self, *args):

# the centroiding returns an ndarray
# so we're initialiazing one here
self.star_list = ndarray((0, 2))
self.star_list = np.empty((0, 2))
self.highlight_count = 0

def set_exp(self, option):
Expand Down Expand Up @@ -139,8 +141,8 @@ def draw_star_selectors(self):

for _i in range(self.highlight_count):
raw_y, raw_x = self.star_list[_i]
star_x = int(raw_x / 2)
star_y = int(raw_y / 2)
star_x = int(raw_x / 4)
star_y = int(raw_y / 4)

x_direction = 1
x_text_offset = 6
Expand Down Expand Up @@ -188,13 +190,9 @@ def update(self, force=False):
# Fetch Centroids before image is altered
# Do this at least once to get a numpy array in
# star_list
if self.align_mode:
cent_image_obj = image_obj.resize((256, 256))
_t = time.time()
self.star_list = tetra3.get_centroids_from_image(
cent_image_obj,
sigma_mode="local_median_abs",
filtsize=11,
if self.align_mode and self.shared_state and self.shared_state.solution():
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll have to dig into this a bit more.... but it seems like this would only allow alignment if the solver is working (which may not be that bad an idea). Also, would a planet show up in the matched_centroids key? The current code just matches anything bright for alignment, even if it's not a match to the star-patterns

self.star_list = np.array(
self.shared_state.solution()["matched_centroids"]
)

# Resize
Expand Down
28 changes: 28 additions & 0 deletions python/PiFinder/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,35 @@ def create_path(apath: Path):
cwd_dir = Path.cwd()
pifinder_dir = Path("..")
astro_data_dir = pifinder_dir / "astro_data"
tetra3_dir = pifinder_dir / "python/PiFinder/tetra3/tetra3"
data_dir = Path(Path.home(), "PiFinder_data")
pifinder_db = astro_data_dir / "pifinder_objects.db"
observations_db = data_dir / "observations.db"
debug_dump_dir = data_dir / "solver_debug_dumps"


def get_os_info():
import platform

platform_system = platform.system()

# Get the architecture (e.g., '64bit', 'ARM')
architecture = platform.machine()

# For more details, including the specific distribution on Linux
if platform_system == "Linux":
lib = "N/A"
version = "N/A"
libc_ver = (lib, version)
try:
libc_ver = platform.libc_ver(lib=lib, version=version)
except AttributeError:
pass
os_detail = f"{platform_system} ({libc_ver[0]} {libc_ver[1]})"
elif platform_system == "Darwin":
os_detail = f"macOS ({platform.mac_ver()[0]})"
elif platform_system == "Windows":
os_detail = f"Windows ({platform.win32_ver()})"
else:
os_detail = "N/A"
return os_detail, platform_system, architecture
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@ sh==1.14.3
skyfield==1.45
timezonefinder==6.1.9
tqdm==4.65.0
alpyca
grpcio==1.60.0
protobuf==4.25.2
1 change: 1 addition & 0 deletions test_images/debug.png
Binary file added test_images/orion_bad.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test_images/pleiades.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test_images/random.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading